2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 #include "cl_dyntexture.h"
29 #include "ft2_fontdefs.h"
32 static mempool_t *fonts_mempool = NULL;
34 cvar_t r_textshadow = {CVAR_SAVE, "r_textshadow", "0", "draws a shadow on all text to improve readability (note: value controls offset, 1 = 1 pixel, 1.5 = 1.5 pixels, etc)"};
35 cvar_t r_textbrightness = {CVAR_SAVE, "r_textbrightness", "0", "additional brightness for text color codes (0 keeps colors as is, 1 makes them all white)"};
36 cvar_t r_textcontrast = {CVAR_SAVE, "r_textcontrast", "1", "additional contrast for text color codes (1 keeps colors as is, 0 makes them all black)"};
38 cvar_t r_font_postprocess_blur = {CVAR_SAVE, "r_font_postprocess_blur", "0", "font blur amount"};
39 cvar_t r_font_postprocess_outline = {CVAR_SAVE, "r_font_postprocess_outline", "0", "font outline amount"};
40 cvar_t r_font_postprocess_shadow_x = {CVAR_SAVE, "r_font_postprocess_shadow_x", "0", "font shadow X shift amount, applied during outlining"};
41 cvar_t r_font_postprocess_shadow_y = {CVAR_SAVE, "r_font_postprocess_shadow_y", "0", "font shadow Y shift amount, applied during outlining"};
42 cvar_t r_font_postprocess_shadow_z = {CVAR_SAVE, "r_font_postprocess_shadow_z", "0", "font shadow Z shift amount, applied during blurring"};
43 cvar_t r_font_hinting = {CVAR_SAVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"};
44 cvar_t r_font_antialias = {CVAR_SAVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */};
46 extern cvar_t v_glslgamma;
48 //=============================================================================
49 /* Support Routines */
51 #define FONT_FILESIZE 13468
52 static cachepic_t *cachepichash[CACHEPICHASHSIZE];
53 static cachepic_t cachepics[MAX_CACHED_PICS];
54 static int numcachepics;
56 rtexturepool_t *drawtexturepool;
58 static const unsigned char concharimage[FONT_FILESIZE] =
63 static rtexture_t *draw_generateconchars(void)
70 data = LoadTGA_BGRA (concharimage, FONT_FILESIZE, NULL);
72 for (i = 0;i < 8192;i++)
74 random = lhrandom (0.0,1.0);
75 data[i*4+3] = data[i*4+0];
76 data[i*4+2] = 83 + (unsigned char)(random * 64);
77 data[i*4+1] = 71 + (unsigned char)(random * 32);
78 data[i*4+0] = 23 + (unsigned char)(random * 16);
81 for (i = 8192;i < 32768;i++)
83 random = lhrandom (0.0,1.0);
84 data[i*4+3] = data[i*4+0];
85 data[i*4+2] = 95 + (unsigned char)(random * 64);
86 data[i*4+1] = 95 + (unsigned char)(random * 64);
87 data[i*4+0] = 95 + (unsigned char)(random * 64);
90 for (i = 32768;i < 40960;i++)
92 random = lhrandom (0.0,1.0);
93 data[i*4+3] = data[i*4+0];
94 data[i*4+2] = 83 + (unsigned char)(random * 64);
95 data[i*4+1] = 71 + (unsigned char)(random * 32);
96 data[i*4+0] = 23 + (unsigned char)(random * 16);
99 for (i = 40960;i < 65536;i++)
101 random = lhrandom (0.0,1.0);
102 data[i*4+3] = data[i*4+0];
103 data[i*4+2] = 96 + (unsigned char)(random * 64);
104 data[i*4+1] = 43 + (unsigned char)(random * 32);
105 data[i*4+0] = 27 + (unsigned char)(random * 32);
109 Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, data);
112 tex = R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, data, TEXTYPE_BGRA, TEXF_ALPHA, -1, NULL);
117 static rtexture_t *draw_generateditherpattern(void)
120 unsigned char pixels[8][8];
121 for (y = 0;y < 8;y++)
122 for (x = 0;x < 8;x++)
123 pixels[y][x] = ((x^y) & 4) ? 254 : 0;
124 return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST, -1, palette_bgra_transparent);
127 typedef struct embeddedpic_s
136 static const embeddedpic_t embeddedpics[] =
139 "gfx/prydoncursor001", 16, 16,
158 "ui/mousepointer", 16, 16,
177 "gfx/crosshair1", 16, 16,
196 "gfx/crosshair2", 16, 16,
215 "gfx/crosshair3", 16, 16,
234 "gfx/crosshair4", 16, 16,
253 "gfx/crosshair5", 8, 8,
264 "gfx/crosshair6", 2, 2,
269 "gfx/crosshair7", 16, 16,
290 static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
292 const embeddedpic_t *p;
293 for (p = embeddedpics;p->name;p++)
294 if (!strcmp(name, p->name))
295 return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA, -1, palette_bgra_embeddedpic);
296 if (!strcmp(name, "gfx/conchars"))
297 return draw_generateconchars();
298 if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
299 return draw_generateditherpattern();
301 Con_DPrintf("Draw_CachePic: failed to load %s\n", name);
302 return r_texture_notexture;
312 // FIXME: move this to client somehow
313 cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
316 unsigned char *pixels = NULL;
319 unsigned char *lmpdata;
320 char lmpname[MAX_QPATH];
323 qboolean ddshasalpha;
324 float ddsavgcolor[4];
325 qboolean loaded = false;
327 texflags = TEXF_ALPHA;
328 if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
329 texflags |= TEXF_CLAMP;
330 if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer && gl_texturecompression.integer)
331 texflags |= TEXF_COMPRESS;
333 // check whether the picture has already been cached
334 crc = CRC_Block((unsigned char *)path, strlen(path));
335 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
336 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
338 if (!strcmp (path, pic->name))
340 // if it was created (or replaced) by Draw_NewPic, just return it
341 if(pic->flags & CACHEPICFLAG_NEWPIC)
343 if (!((pic->texflags ^ texflags) & ~(TEXF_COMPRESS))) // ignore TEXF_COMPRESS when comparing, because fallback pics remove the flag
345 if(!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
348 pic->autoload = false; // persist it
350 goto reload; // load it below, and then persist
357 if (numcachepics == MAX_CACHED_PICS)
359 Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
360 // FIXME: support NULL in callers?
361 return cachepics; // return the first one
363 pic = cachepics + (numcachepics++);
364 memset(pic, 0, sizeof(*pic));
365 strlcpy (pic->name, path, sizeof(pic->name));
367 pic->chain = cachepichash[hashkey];
368 cachepichash[hashkey] = pic;
371 // TODO why does this crash?
372 if(pic->allow_free_tex && pic->tex)
373 R_PurgeTexture(pic->tex);
375 // check whether it is an dynamic texture (if so, we can directly use its texture handler)
376 pic->flags = cachepicflags;
377 pic->tex = CL_GetDynTexture( path );
378 // if so, set the width/height, too
380 pic->allow_free_tex = false;
381 pic->width = R_TextureWidth(pic->tex);
382 pic->height = R_TextureHeight(pic->tex);
383 // we're done now (early-out)
387 pic->allow_free_tex = true;
389 pic->hasalpha = true; // assume alpha unless we know it has none
390 pic->texflags = texflags;
391 pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
392 pic->lastusedframe = draw_frame;
394 // load a high quality image from disk if possible
395 if (!loaded && r_texture_dds_load.integer != 0 && (pic->tex = R_LoadTextureDDSFile(drawtexturepool, va("dds/%s.dds", pic->name), vid.sRGB2D, pic->texflags, &ddshasalpha, ddsavgcolor, 0)))
397 // note this loads even if autoload is true, otherwise we can't get the width/height
399 pic->hasalpha = ddshasalpha;
400 pic->width = R_TextureWidth(pic->tex);
401 pic->height = R_TextureHeight(pic->tex);
403 if (!loaded && ((pixels = loadimagepixelsbgra(pic->name, false, true, false, NULL)) || (!strncmp(pic->name, "gfx/", 4) && (pixels = loadimagepixelsbgra(pic->name+4, false, true, false, NULL)))))
406 pic->hasalpha = false;
407 if (pic->texflags & TEXF_ALPHA)
409 for (j = 3;j < image_width * image_height * 4;j += 4)
413 pic->hasalpha = true;
419 pic->width = image_width;
420 pic->height = image_height;
423 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, image_width, image_height, pixels, vid.sRGB2D ? TEXTYPE_SRGB_BGRA : TEXTYPE_BGRA, pic->texflags & (pic->hasalpha ? ~0 : ~TEXF_ALPHA), -1, NULL);
425 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
426 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
432 pic->autoload = false;
433 // never compress the fallback images
434 pic->texflags &= ~TEXF_COMPRESS;
437 // now read the low quality version (wad or lmp file), and take the pic
438 // size from that even if we don't upload the texture, this way the pics
439 // show up the right size in the menu even if they were replaced with
440 // higher or lower resolution versions
441 dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", pic->name);
442 if (!strncmp(pic->name, "gfx/", 4) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
444 if (developer_loading.integer)
445 Con_Printf("loading lump \"%s\"\n", pic->name);
449 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
450 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
451 // if no high quality replacement image was found, upload the original low quality texture
455 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, pic->width, pic->height, lmpdata + 8, vid.sRGB2D ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
460 else if ((lmpdata = W_GetLumpName (pic->name + 4)))
462 if (developer_loading.integer)
463 Con_Printf("loading gfx.wad lump \"%s\"\n", pic->name + 4);
465 if (!strcmp(pic->name, "gfx/conchars"))
467 // conchars is a raw image and with color 0 as transparent instead of 255
470 // if no high quality replacement image was found, upload the original low quality texture
474 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, 128, 128, lmpdata, vid.sRGB2D != 0 ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
479 pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
480 pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
481 // if no high quality replacement image was found, upload the original low quality texture
485 pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, pic->width, pic->height, lmpdata + 8, vid.sRGB2D != 0 ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_transparent);
497 // if it's not found on disk, generate an image
498 pic->tex = draw_generatepic(pic->name, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
499 pic->width = R_TextureWidth(pic->tex);
500 pic->height = R_TextureHeight(pic->tex);
501 pic->allow_free_tex = (pic->tex != r_texture_notexture);
507 cachepic_t *Draw_CachePic (const char *path)
509 return Draw_CachePic_Flags (path, 0); // default to persistent!
512 rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
514 if (pic->autoload && !pic->tex)
516 if (pic->tex == NULL && r_texture_dds_load.integer != 0)
518 qboolean ddshasalpha;
519 float ddsavgcolor[4];
520 pic->tex = R_LoadTextureDDSFile(drawtexturepool, va("dds/%s.dds", pic->name), vid.sRGB2D, pic->texflags, &ddshasalpha, ddsavgcolor, 0);
522 if (pic->tex == NULL)
524 pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, vid.sRGB2D);
526 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
527 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
530 if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
532 pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, vid.sRGB2D);
534 if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
535 R_SaveTextureDDSFile(pic->tex, va("dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
538 if (pic->tex == NULL)
539 pic->tex = draw_generatepic(pic->name, true);
541 pic->lastusedframe = draw_frame;
545 void Draw_Frame(void)
549 static double nextpurgetime;
550 if (nextpurgetime > realtime)
552 nextpurgetime = realtime + 0.05;
553 for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
555 if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
557 R_FreeTexture(pic->tex);
564 cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
569 crc = CRC_Block((unsigned char *)picname, strlen(picname));
570 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
571 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
572 if (!strcmp (picname, pic->name))
577 if (pic->flags == CACHEPICFLAG_NEWPIC && pic->tex && pic->width == width && pic->height == height)
579 R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, 0, width, height, 1);
585 if (numcachepics == MAX_CACHED_PICS)
587 Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
588 // FIXME: support NULL in callers?
589 return cachepics; // return the first one
591 pic = cachepics + (numcachepics++);
592 memset(pic, 0, sizeof(*pic));
593 strlcpy (pic->name, picname, sizeof(pic->name));
595 pic->chain = cachepichash[hashkey];
596 cachepichash[hashkey] = pic;
599 pic->flags = CACHEPICFLAG_NEWPIC; // disable texflags checks in Draw_CachePic
601 pic->height = height;
602 if (pic->allow_free_tex && pic->tex)
603 R_FreeTexture(pic->tex);
604 pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0), -1, NULL);
608 void Draw_FreePic(const char *picname)
613 // this doesn't really free the pic, but does free it's texture
614 crc = CRC_Block((unsigned char *)picname, strlen(picname));
615 hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
616 for (pic = cachepichash[hashkey];pic;pic = pic->chain)
618 if (!strcmp (picname, pic->name) && pic->tex)
620 R_FreeTexture(pic->tex);
629 static float snap_to_pixel_x(float x, float roundUpAt);
630 extern int con_linewidth; // to force rewrapping
631 void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
635 char widthfile[MAX_QPATH];
637 fs_offset_t widthbufsize;
639 if(override || !fnt->texpath[0])
641 strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
642 // load the cvars when the font is FIRST loader
643 fnt->settings.scale = scale;
644 fnt->settings.voffset = voffset;
645 fnt->settings.antialias = r_font_antialias.integer;
646 fnt->settings.hinting = r_font_hinting.integer;
647 fnt->settings.outline = r_font_postprocess_outline.value;
648 fnt->settings.blur = r_font_postprocess_blur.value;
649 fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
650 fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
651 fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
654 if (fnt->settings.scale <= 0)
655 fnt->settings.scale = 1;
657 if(drawtexturepool == NULL)
658 return; // before gl_draw_start, so will be loaded later
662 // clear freetype font
663 Font_UnloadFont(fnt->ft2);
668 if(fnt->req_face != -1)
670 if(!Font_LoadFont(fnt->texpath, fnt))
671 Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
674 fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
675 if(fnt->tex == r_texture_notexture)
677 for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
679 if (!fnt->fallbacks[i][0])
681 fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION)->tex;
682 if(fnt->tex != r_texture_notexture)
685 if(fnt->tex == r_texture_notexture)
687 fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION)->tex;
688 strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
691 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
694 dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
696 // unspecified width == 1 (base width)
697 for(ch = 0; ch < 256; ++ch)
698 fnt->width_of[ch] = 1;
700 // FIXME load "name.width", if it fails, fill all with 1
701 if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
703 float extraspacing = 0;
704 const char *p = widthbuf;
709 if(!COM_ParseToken_Simple(&p, false, false, true))
727 fnt->width_of[ch] = atof(com_token) + extraspacing;
731 if(!strcmp(com_token, "extraspacing"))
733 if(!COM_ParseToken_Simple(&p, false, false, true))
735 extraspacing = atof(com_token);
737 else if(!strcmp(com_token, "scale"))
739 if(!COM_ParseToken_Simple(&p, false, false, true))
741 fnt->settings.scale = atof(com_token);
745 Con_Printf("Warning: skipped unknown font property %s\n", com_token);
746 if(!COM_ParseToken_Simple(&p, false, false, true))
758 for (i = 0; i < MAX_FONT_SIZES; ++i)
760 ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
763 for(ch = 0; ch < 256; ++ch)
764 map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
768 maxwidth = fnt->width_of[0];
769 for(i = 1; i < 256; ++i)
770 maxwidth = max(maxwidth, fnt->width_of[i]);
771 fnt->maxwidth = maxwidth;
773 // fix up maxwidth for overlap
774 fnt->maxwidth *= fnt->settings.scale;
776 if(fnt == FONT_CONSOLE)
777 con_linewidth = -1; // rewrap console in next frame
780 extern cvar_t developer_font;
781 dp_font_t *FindFont(const char *title, qboolean allocate_new)
786 for(i = 0; i < dp_fonts.maxsize; ++i)
787 if(!strcmp(dp_fonts.f[i].title, title))
788 return &dp_fonts.f[i];
789 // if not found - try allocate
792 // find any font with empty title
793 for(i = 0; i < dp_fonts.maxsize; ++i)
795 if(!strcmp(dp_fonts.f[i].title, ""))
797 strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
798 return &dp_fonts.f[i];
801 // if no any 'free' fonts - expand buffer
802 oldsize = dp_fonts.maxsize;
803 dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
804 if (developer_font.integer)
805 Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", oldsize, dp_fonts.maxsize);
806 dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
807 // relink ft2 structures
808 for(i = 0; i < oldsize; ++i)
809 if (dp_fonts.f[i].ft2)
810 dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings;
811 // register a font in first expanded slot
812 strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
813 return &dp_fonts.f[oldsize];
818 static float snap_to_pixel_x(float x, float roundUpAt)
820 float pixelpos = x * vid.width / vid_conwidth.value;
821 int snap = (int) pixelpos;
822 if (pixelpos - snap >= roundUpAt) ++snap;
823 return ((float)snap * vid_conwidth.value / vid.width);
825 x = (int)(x * vid.width / vid_conwidth.value);
826 x = (x * vid_conwidth.value / vid.width);
831 static float snap_to_pixel_y(float y, float roundUpAt)
833 float pixelpos = y * vid.height / vid_conheight.value;
834 int snap = (int) pixelpos;
835 if (pixelpos - snap > roundUpAt) ++snap;
836 return ((float)snap * vid_conheight.value / vid.height);
838 y = (int)(y * vid.height / vid_conheight.value);
839 y = (y * vid_conheight.value / vid.height);
844 static void LoadFont_f(void)
848 const char *filelist, *c, *cm;
849 float sz, scale, voffset;
850 char mainfont[MAX_QPATH];
854 Con_Printf("Available font commands:\n");
855 for(i = 0; i < dp_fonts.maxsize; ++i)
856 if (dp_fonts.f[i].title[0])
857 Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
858 Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
859 "can specify multiple fonts and faces\n"
860 "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
861 "to load face 2 of the font gfx/vera-sans and use face 1\n"
862 "of gfx/fallback as fallback font.\n"
863 "You can also specify a list of font sizes to load, like this:\n"
864 "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
865 "In many cases, 8 12 16 24 32 should be a good choice.\n"
867 " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
868 " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
872 f = FindFont(Cmd_Argv(1), true);
875 Con_Printf("font function not found\n");
880 filelist = "gfx/conchars";
882 filelist = Cmd_Argv(2);
884 memset(f->fallbacks, 0, sizeof(f->fallbacks));
885 memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
887 // first font is handled "normally"
888 c = strchr(filelist, ':');
889 cm = strchr(filelist, ',');
890 if(c && (!cm || c < cm))
891 f->req_face = atoi(c+1);
898 if(!c || (c - filelist) > MAX_QPATH)
899 strlcpy(mainfont, filelist, sizeof(mainfont));
902 memcpy(mainfont, filelist, c - filelist);
903 mainfont[c - filelist] = 0;
906 for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
908 c = strchr(filelist, ',');
914 c = strchr(filelist, ':');
915 cm = strchr(filelist, ',');
916 if(c && (!cm || c < cm))
917 f->fallback_faces[i] = atoi(c+1);
920 f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
923 if(!c || (c-filelist) > MAX_QPATH)
925 strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
929 memcpy(f->fallbacks[i], filelist, c - filelist);
930 f->fallbacks[i][c - filelist] = 0;
934 // for now: by default load only one size: the default size
936 for(i = 1; i < MAX_FONT_SIZES; ++i)
937 f->req_sizes[i] = -1;
943 for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
946 if (!strcmp(Cmd_Argv(i), "scale"))
950 scale = atof(Cmd_Argv(i));
953 if (!strcmp(Cmd_Argv(i), "voffset"))
957 voffset = atof(Cmd_Argv(i));
962 continue; // no slot for other sizes
964 // parse one of sizes
965 sz = atof(Cmd_Argv(i));
966 if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
968 // search for duplicated sizes
970 for (j=0; j<sizes; j++)
971 if (f->req_sizes[j] == sz)
974 continue; // sz already in req_sizes, don't add it again
976 if (sizes == MAX_FONT_SIZES)
978 Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
982 f->req_sizes[sizes] = sz;
988 LoadFont(true, mainfont, f, scale, voffset);
996 static void gl_draw_start(void)
999 drawtexturepool = R_AllocTexturePool();
1002 memset(cachepichash, 0, sizeof(cachepichash));
1006 // load default font textures
1007 for(i = 0; i < dp_fonts.maxsize; ++i)
1008 if (dp_fonts.f[i].title[0])
1009 LoadFont(false, va("gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
1011 // draw the loading screen so people have something to see in the newly opened window
1012 SCR_UpdateLoadingScreen(true);
1015 static void gl_draw_shutdown(void)
1019 R_FreeTexturePool(&drawtexturepool);
1022 memset(cachepichash, 0, sizeof(cachepichash));
1025 static void gl_draw_newmap(void)
1030 void GL_Draw_Init (void)
1034 Cvar_RegisterVariable(&r_font_postprocess_blur);
1035 Cvar_RegisterVariable(&r_font_postprocess_outline);
1036 Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
1037 Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
1038 Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
1039 Cvar_RegisterVariable(&r_font_hinting);
1040 Cvar_RegisterVariable(&r_font_antialias);
1041 Cvar_RegisterVariable(&r_textshadow);
1042 Cvar_RegisterVariable(&r_textbrightness);
1043 Cvar_RegisterVariable(&r_textcontrast);
1045 // allocate fonts storage
1046 fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
1047 dp_fonts.maxsize = MAX_FONTS;
1048 dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
1049 memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
1051 // assign starting font names
1052 strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
1053 strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
1054 strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
1055 strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
1056 strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
1057 strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
1058 strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
1059 strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
1060 strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
1061 for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
1062 if(!FONT_USER(i)->title[0])
1063 dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
1065 Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
1066 R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
1069 static void _DrawQ_Setup(void) // see R_ResetViewRendering2D
1071 r_viewport_t viewport;
1072 if (r_refdef.draw2dstage == 1)
1074 r_refdef.draw2dstage = 1;
1077 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);
1078 R_Mesh_SetRenderTargets(0, NULL, NULL, NULL, NULL, NULL);
1079 R_SetViewport(&viewport);
1080 //GL_Scissor(viewport.x, viewport.y, viewport.width, viewport.height); // DrawQ_SetClipArea would do this
1081 GL_Color(1, 1, 1, 1);
1082 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
1083 //GL_BlendFunc(GL_ONE, GL_ZERO); // DrawQ_ProcessDrawFlag does this
1084 GL_ScissorTest(false);
1085 GL_DepthMask(false);
1086 GL_DepthRange(0, 1);
1087 GL_DepthTest(false);
1088 GL_DepthFunc(GL_LEQUAL);
1089 R_EntityMatrix(&identitymatrix);
1090 R_Mesh_ResetTextureState();
1091 GL_PolygonOffset(0, 0);
1092 //R_SetStencil(false, 255, GL_KEEP, GL_KEEP, GL_KEEP, GL_ALWAYS, 128, 255); // not needed
1093 //qglEnable(GL_POLYGON_OFFSET_FILL); // we never use polygon offset here
1094 GL_CullFace(GL_NONE);
1097 qboolean r_draw2d_force = false;
1098 void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
1102 if(!r_draw2d.integer && !r_draw2d_force)
1104 DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
1106 void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
1108 if(flags == DRAWFLAG_ADDITIVE)
1110 GL_DepthMask(false);
1111 GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
1113 else if(flags == DRAWFLAG_MODULATE)
1115 GL_DepthMask(false);
1116 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
1118 else if(flags == DRAWFLAG_2XMODULATE)
1120 GL_DepthMask(false);
1121 GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
1123 else if(flags == DRAWFLAG_SCREEN)
1125 GL_DepthMask(false);
1126 GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
1130 GL_DepthMask(false);
1131 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1136 GL_BlendFunc(GL_ONE, GL_ZERO);
1140 void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
1144 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1145 if(!r_draw2d.integer && !r_draw2d_force)
1148 // R_Mesh_ResetTextureState();
1149 floats[12] = 0.0f;floats[13] = 0.0f;
1150 floats[14] = 1.0f;floats[15] = 0.0f;
1151 floats[16] = 1.0f;floats[17] = 1.0f;
1152 floats[18] = 0.0f;floats[19] = 1.0f;
1153 floats[20] = floats[24] = floats[28] = floats[32] = red;
1154 floats[21] = floats[25] = floats[29] = floats[33] = green;
1155 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1156 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1162 height = pic->height;
1163 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1166 // AK07: lets be texel correct on the corners
1168 float horz_offset = 0.5f / pic->width;
1169 float vert_offset = 0.5f / pic->height;
1171 floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
1172 floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
1173 floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
1174 floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
1179 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1181 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1182 floats[0] = floats[9] = x;
1183 floats[1] = floats[4] = y;
1184 floats[3] = floats[6] = x + width;
1185 floats[7] = floats[10] = y + height;
1187 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1188 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1191 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)
1194 float af = DEG2RAD(-angle); // forward
1195 float ar = DEG2RAD(-angle + 90); // right
1196 float sinaf = sin(af);
1197 float cosaf = cos(af);
1198 float sinar = sin(ar);
1199 float cosar = cos(ar);
1201 _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
1202 if(!r_draw2d.integer && !r_draw2d_force)
1205 // R_Mesh_ResetTextureState();
1211 height = pic->height;
1212 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1215 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1217 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1220 floats[0] = x - cosaf*org_x - cosar*org_y;
1221 floats[1] = y - sinaf*org_x - sinar*org_y;
1224 floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
1225 floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
1228 floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
1229 floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
1232 floats[9] = x - cosaf*org_x + cosar*(height-org_y);
1233 floats[10] = y - sinaf*org_x + sinar*(height-org_y);
1235 floats[12] = 0.0f;floats[13] = 0.0f;
1236 floats[14] = 1.0f;floats[15] = 0.0f;
1237 floats[16] = 1.0f;floats[17] = 1.0f;
1238 floats[18] = 0.0f;floats[19] = 1.0f;
1239 floats[20] = floats[24] = floats[28] = floats[32] = red;
1240 floats[21] = floats[25] = floats[29] = floats[33] = green;
1241 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1242 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1244 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1245 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1248 void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
1252 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
1253 if(!r_draw2d.integer && !r_draw2d_force)
1256 // R_Mesh_ResetTextureState();
1257 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1259 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1260 floats[0] = floats[9] = x;
1261 floats[1] = floats[4] = y;
1262 floats[3] = floats[6] = x + width;
1263 floats[7] = floats[10] = y + height;
1264 floats[12] = 0.0f;floats[13] = 0.0f;
1265 floats[14] = 1.0f;floats[15] = 0.0f;
1266 floats[16] = 1.0f;floats[17] = 1.0f;
1267 floats[18] = 0.0f;floats[19] = 1.0f;
1268 floats[20] = floats[24] = floats[28] = floats[32] = red;
1269 floats[21] = floats[25] = floats[29] = floats[33] = green;
1270 floats[22] = floats[26] = floats[30] = floats[34] = blue;
1271 floats[23] = floats[27] = floats[31] = floats[35] = alpha;
1273 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1274 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1277 /// color tag printing
1278 static const vec4_t string_colors[] =
1281 // LordHavoc: why on earth is cyan before magenta in Quake3?
1282 // LordHavoc: note: Doom3 uses white for [0] and [7]
1283 {0.0, 0.0, 0.0, 1.0}, // black
1284 {1.0, 0.0, 0.0, 1.0}, // red
1285 {0.0, 1.0, 0.0, 1.0}, // green
1286 {1.0, 1.0, 0.0, 1.0}, // yellow
1287 {0.0, 0.0, 1.0, 1.0}, // blue
1288 {0.0, 1.0, 1.0, 1.0}, // cyan
1289 {1.0, 0.0, 1.0, 1.0}, // magenta
1290 {1.0, 1.0, 1.0, 1.0}, // white
1291 // [515]'s BX_COLOREDTEXT extension
1292 {1.0, 1.0, 1.0, 0.5}, // half transparent
1293 {0.5, 0.5, 0.5, 1.0} // half brightness
1294 // Black's color table
1295 //{1.0, 1.0, 1.0, 1.0},
1296 //{1.0, 0.0, 0.0, 1.0},
1297 //{0.0, 1.0, 0.0, 1.0},
1298 //{0.0, 0.0, 1.0, 1.0},
1299 //{1.0, 1.0, 0.0, 1.0},
1300 //{0.0, 1.0, 1.0, 1.0},
1301 //{1.0, 0.0, 1.0, 1.0},
1302 //{0.1, 0.1, 0.1, 1.0}
1305 #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
1307 static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
1309 float C = r_textcontrast.value;
1310 float B = r_textbrightness.value;
1311 if (colorindex & 0x10000) // that bit means RGB color
1313 color[0] = ((colorindex >> 12) & 0xf) / 15.0;
1314 color[1] = ((colorindex >> 8) & 0xf) / 15.0;
1315 color[2] = ((colorindex >> 4) & 0xf) / 15.0;
1316 color[3] = (colorindex & 0xf) / 15.0;
1319 Vector4Copy(string_colors[colorindex], color);
1320 Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
1323 float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
1324 Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
1328 // NOTE: this function always draws exactly one character if maxwidth <= 0
1329 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)
1331 const char *text_start = text;
1332 int colorindex = STRING_COLOR_DEFAULT;
1335 Uchar ch, mapch, nextch;
1336 Uchar prevch = 0; // used for kerning
1341 ft2_font_map_t *fontmap = NULL;
1342 ft2_font_map_t *map = NULL;
1343 //ft2_font_map_t *prevmap = NULL;
1344 ft2_font_t *ft2 = fnt->ft2;
1346 qboolean snap = true;
1347 qboolean least_one = false;
1348 float dw; // display w
1349 //float dh; // display h
1350 const float *width_of;
1357 // do this in the end
1358 w *= fnt->settings.scale;
1359 h *= fnt->settings.scale;
1361 // find the most fitting size:
1365 map_index = Font_IndexForSize(ft2, h, &w, &h);
1367 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1368 fontmap = Font_MapForIndex(ft2, map_index);
1377 if (!outcolor || *outcolor == -1)
1378 colorindex = STRING_COLOR_DEFAULT;
1380 colorindex = *outcolor;
1382 // maxwidth /= fnt->scale; // w and h are multiplied by it already
1383 // ftbase_x = snap_to_pixel_x(0);
1388 maxwidth = -maxwidth;
1392 // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
1395 width_of = fontmap->width_of;
1397 width_of = fnt->width_of;
1399 for (i = 0;((bytes_left = *maxlen - (text - text_start)) > 0) && *text;)
1402 nextch = ch = u8_getnchar(text, &text, bytes_left);
1403 i = text - text_start;
1406 if (ch == ' ' && !fontmap)
1408 if(!least_one || i0) // never skip the first character
1409 if(x + width_of[(int) ' '] * dw > maxwidth)
1412 break; // oops, can't draw this
1414 x += width_of[(int) ' '] * dw;
1417 // i points to the char after ^
1418 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
1420 ch = *text; // colors are ascii, so no u8_ needed
1421 if (ch <= '9' && ch >= '0') // ^[0-9] found
1423 colorindex = ch - '0';
1428 // i points to the char after ^...
1429 // i+3 points to 3 in ^x123
1430 // i+3 == *maxlen would mean that char is missing
1431 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
1433 // building colorindex...
1434 ch = tolower(text[1]);
1435 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1436 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1437 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1438 else tempcolorindex = 0;
1441 ch = tolower(text[2]);
1442 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1443 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1444 else tempcolorindex = 0;
1447 ch = tolower(text[3]);
1448 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1449 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1450 else tempcolorindex = 0;
1453 colorindex = tempcolorindex | 0xf;
1454 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1462 else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
1471 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1478 map = ft2_oldstyle_map;
1480 if(!least_one || i0) // never skip the first character
1481 if(x + width_of[ch] * dw > maxwidth)
1484 break; // oops, can't draw this
1486 x += width_of[ch] * dw;
1488 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1490 map = FontMap_FindForChar(fontmap, ch);
1493 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1499 mapch = ch - map->start;
1500 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
1502 x += map->glyphs[mapch].advance_x * dw;
1511 *outcolor = colorindex;
1516 float DrawQ_Color[4];
1517 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)
1519 int shadow, colorindex = STRING_COLOR_DEFAULT;
1521 float x = startx, y, s, t, u, v, thisw;
1522 float *av, *at, *ac;
1524 static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
1525 static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
1526 static float color4f[QUADELEMENTS_MAXQUADS*4*4];
1527 Uchar ch, mapch, nextch;
1528 Uchar prevch = 0; // used for kerning
1531 //ft2_font_map_t *prevmap = NULL; // the previous map
1532 ft2_font_map_t *map = NULL; // the currently used map
1533 ft2_font_map_t *fontmap = NULL; // the font map for the size
1535 const char *text_start = text;
1537 ft2_font_t *ft2 = fnt->ft2;
1538 qboolean snap = true;
1542 const float *width_of;
1545 tw = R_TextureWidth(fnt->tex);
1546 th = R_TextureHeight(fnt->tex);
1554 starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
1555 w *= fnt->settings.scale;
1556 h *= fnt->settings.scale;
1561 map_index = Font_IndexForSize(ft2, h, &w, &h);
1563 map_index = Font_IndexForSize(ft2, h, NULL, NULL);
1564 fontmap = Font_MapForIndex(ft2, map_index);
1570 // draw the font at its baseline when using freetype
1572 ftbase_y = dh * (4.5/6.0);
1577 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
1578 if(!r_draw2d.integer && !r_draw2d_force)
1579 return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
1581 // R_Mesh_ResetTextureState();
1583 R_Mesh_TexBind(0, fnt->tex);
1584 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1591 //ftbase_x = snap_to_pixel_x(ftbase_x);
1594 startx = snap_to_pixel_x(startx, 0.4);
1595 starty = snap_to_pixel_y(starty, 0.4);
1596 ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
1599 pix_x = vid.width / vid_conwidth.value;
1600 pix_y = vid.height / vid_conheight.value;
1603 width_of = fontmap->width_of;
1605 width_of = fnt->width_of;
1607 for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
1612 if (!outcolor || *outcolor == -1)
1613 colorindex = STRING_COLOR_DEFAULT;
1615 colorindex = *outcolor;
1617 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1624 x += r_textshadow.value * vid.width / vid_conwidth.value;
1625 y += r_textshadow.value * vid.height / vid_conheight.value;
1628 for (i = 0;((bytes_left = maxlen - (text - text_start)) > 0) && *text;)
1630 nextch = ch = u8_getnchar(text, &text, bytes_left);
1631 i = text - text_start;
1634 if (ch == ' ' && !fontmap)
1636 x += width_of[(int) ' '] * dw;
1639 if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
1641 ch = *text; // colors are ascii, so no u8_ needed
1642 if (ch <= '9' && ch >= '0') // ^[0-9] found
1644 colorindex = ch - '0';
1645 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1650 else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
1652 // building colorindex...
1653 ch = tolower(text[1]);
1654 tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
1655 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
1656 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
1657 else tempcolorindex = 0;
1660 ch = tolower(text[2]);
1661 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
1662 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
1663 else tempcolorindex = 0;
1666 ch = tolower(text[3]);
1667 if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
1668 else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
1669 else tempcolorindex = 0;
1672 colorindex = tempcolorindex | 0xf;
1673 // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
1674 //Con_Printf("^1colorindex:^7 %x\n", colorindex);
1675 DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
1683 else if (ch == STRING_COLOR_TAG)
1692 // using a value of -1 for the oldstyle map because NULL means uninitialized...
1693 // this way we don't need to rebind fnt->tex for every old-style character
1694 // E000..E0FF: emulate old-font characters (to still have smileys and such available)
1697 x += 1.0/pix_x * r_textshadow.value;
1698 y += 1.0/pix_y * r_textshadow.value;
1700 if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
1708 if (map != ft2_oldstyle_map)
1712 // switching from freetype to non-freetype rendering
1713 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1714 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1720 R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1721 map = ft2_oldstyle_map;
1725 //num = (unsigned char) text[i];
1726 //thisw = fnt->width_of[num];
1727 thisw = fnt->width_of[ch];
1728 // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
1729 s = (ch & 15)*0.0625f + (0.5f / tw);
1730 t = (ch >> 4)*0.0625f + (0.5f / th);
1731 u = 0.0625f * thisw - (1.0f / tw);
1732 v = 0.0625f - (1.0f / th);
1733 ac[ 0] = DrawQ_Color[0];ac[ 1] = DrawQ_Color[1];ac[ 2] = DrawQ_Color[2];ac[ 3] = DrawQ_Color[3];
1734 ac[ 4] = DrawQ_Color[0];ac[ 5] = DrawQ_Color[1];ac[ 6] = DrawQ_Color[2];ac[ 7] = DrawQ_Color[3];
1735 ac[ 8] = DrawQ_Color[0];ac[ 9] = DrawQ_Color[1];ac[10] = DrawQ_Color[2];ac[11] = DrawQ_Color[3];
1736 ac[12] = DrawQ_Color[0];ac[13] = DrawQ_Color[1];ac[14] = DrawQ_Color[2];ac[15] = DrawQ_Color[3];
1737 at[ 0] = s ; at[ 1] = t ;
1738 at[ 2] = s+u ; at[ 3] = t ;
1739 at[ 4] = s+u ; at[ 5] = t+v ;
1740 at[ 6] = s ; at[ 7] = t+v ;
1741 av[ 0] = x ; av[ 1] = y ; av[ 2] = 10;
1742 av[ 3] = x+dw*thisw ; av[ 4] = y ; av[ 5] = 10;
1743 av[ 6] = x+dw*thisw ; av[ 7] = y+dh ; av[ 8] = 10;
1744 av[ 9] = x ; av[10] = y+dh ; av[11] = 10;
1749 if (batchcount >= QUADELEMENTS_MAXQUADS)
1751 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1752 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1758 x += width_of[ch] * dw;
1760 if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
1762 // new charmap - need to render
1765 // we need a different character map, render what we currently have:
1766 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1767 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1774 map = FontMap_FindForChar(fontmap, ch);
1777 if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
1784 // this shouldn't happen
1789 R_SetupShader_Generic(map->pic->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1792 mapch = ch - map->start;
1793 thisw = map->glyphs[mapch].advance_x;
1797 if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, &ky))
1804 ac[ 0] = DrawQ_Color[0]; ac[ 1] = DrawQ_Color[1]; ac[ 2] = DrawQ_Color[2]; ac[ 3] = DrawQ_Color[3];
1805 ac[ 4] = DrawQ_Color[0]; ac[ 5] = DrawQ_Color[1]; ac[ 6] = DrawQ_Color[2]; ac[ 7] = DrawQ_Color[3];
1806 ac[ 8] = DrawQ_Color[0]; ac[ 9] = DrawQ_Color[1]; ac[10] = DrawQ_Color[2]; ac[11] = DrawQ_Color[3];
1807 ac[12] = DrawQ_Color[0]; ac[13] = DrawQ_Color[1]; ac[14] = DrawQ_Color[2]; ac[15] = DrawQ_Color[3];
1808 at[0] = map->glyphs[mapch].txmin; at[1] = map->glyphs[mapch].tymin;
1809 at[2] = map->glyphs[mapch].txmax; at[3] = map->glyphs[mapch].tymin;
1810 at[4] = map->glyphs[mapch].txmax; at[5] = map->glyphs[mapch].tymax;
1811 at[6] = map->glyphs[mapch].txmin; at[7] = map->glyphs[mapch].tymax;
1812 av[ 0] = x + dw * map->glyphs[mapch].vxmin; av[ 1] = y + dh * map->glyphs[mapch].vymin; av[ 2] = 10;
1813 av[ 3] = x + dw * map->glyphs[mapch].vxmax; av[ 4] = y + dh * map->glyphs[mapch].vymin; av[ 5] = 10;
1814 av[ 6] = x + dw * map->glyphs[mapch].vxmax; av[ 7] = y + dh * map->glyphs[mapch].vymax; av[ 8] = 10;
1815 av[ 9] = x + dw * map->glyphs[mapch].vxmin; av[10] = y + dh * map->glyphs[mapch].vymax; av[11] = 10;
1824 if (batchcount >= QUADELEMENTS_MAXQUADS)
1826 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1827 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1840 x -= 1.0/pix_x * r_textshadow.value;
1841 y -= 1.0/pix_y * r_textshadow.value;
1847 R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
1848 R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
1852 *outcolor = colorindex;
1854 // note: this relies on the proper text (not shadow) being drawn last
1858 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)
1860 return DrawQ_String_Scale(startx, starty, text, maxlen, w, h, 1, 1, basered, basegreen, baseblue, basealpha, flags, outcolor, ignorecolorcodes, fnt);
1863 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)
1865 return DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, maxlen, w, h, 1, 1, outcolor, ignorecolorcodes, fnt, maxwidth);
1868 float DrawQ_TextWidth(const char *text, size_t maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt)
1870 return DrawQ_TextWidth_UntilWidth(text, &maxlen, w, h, ignorecolorcodes, fnt, 1000000000);
1873 float DrawQ_TextWidth_UntilWidth(const char *text, size_t *maxlen, float w, float h, qboolean ignorecolorcodes, const dp_font_t *fnt, float maxWidth)
1875 return DrawQ_TextWidth_UntilWidth_TrackColors(text, maxlen, w, h, NULL, ignorecolorcodes, fnt, maxWidth);
1880 // no ^xrgb management
1881 static int DrawQ_BuildColoredText(char *output2c, size_t maxoutchars, const char *text, int maxreadchars, qboolean ignorecolorcodes, int *outcolor)
1883 int color, numchars = 0;
1884 char *outputend2c = output2c + maxoutchars - 2;
1885 if (!outcolor || *outcolor == -1)
1886 color = STRING_COLOR_DEFAULT;
1890 maxreadchars = 1<<30;
1891 textend = text + maxreadchars;
1892 while (text != textend && *text)
1894 if (*text == STRING_COLOR_TAG && !ignorecolorcodes && text + 1 != textend)
1896 if (text[1] == STRING_COLOR_TAG)
1898 else if (text[1] >= '0' && text[1] <= '9')
1900 color = text[1] - '0';
1905 if (output2c >= outputend2c)
1907 *output2c++ = *text++;
1908 *output2c++ = color;
1911 output2c[0] = output2c[1] = 0;
1918 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)
1922 _DrawQ_SetupAndProcessDrawFlag(flags, pic, a1*a2*a3*a4);
1923 if(!r_draw2d.integer && !r_draw2d_force)
1926 // R_Mesh_ResetTextureState();
1932 height = pic->height;
1933 R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1936 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
1938 floats[2] = floats[5] = floats[8] = floats[11] = 0;
1939 floats[0] = floats[9] = x;
1940 floats[1] = floats[4] = y;
1941 floats[3] = floats[6] = x + width;
1942 floats[7] = floats[10] = y + height;
1943 floats[12] = s1;floats[13] = t1;
1944 floats[14] = s2;floats[15] = t2;
1945 floats[16] = s4;floats[17] = t4;
1946 floats[18] = s3;floats[19] = t3;
1947 floats[20] = r1;floats[21] = g1;floats[22] = b1;floats[23] = a1;
1948 floats[24] = r2;floats[25] = g2;floats[26] = b2;floats[27] = a2;
1949 floats[28] = r4;floats[29] = g4;floats[30] = b4;floats[31] = a4;
1950 floats[32] = r3;floats[33] = g3;floats[34] = b3;floats[35] = a3;
1952 R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
1953 R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
1956 void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags, qboolean hasalpha)
1960 if(!r_draw2d.integer && !r_draw2d_force)
1962 DrawQ_ProcessDrawFlag(flags, hasalpha);
1964 // R_Mesh_ResetTextureState();
1965 R_SetupShader_Generic(mesh->texture, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
1967 R_Mesh_PrepareVertices_Generic_Arrays(mesh->num_vertices, mesh->data_vertex3f, mesh->data_color4f, mesh->data_texcoord2f);
1968 R_Mesh_Draw(0, mesh->num_vertices, 0, mesh->num_triangles, mesh->data_element3i, NULL, 0, mesh->data_element3s, NULL, 0);
1971 void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags)
1975 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 1);
1976 if(!r_draw2d.integer && !r_draw2d_force)
1980 switch(vid.renderpath)
1982 case RENDERPATH_GL11:
1983 case RENDERPATH_GL13:
1984 case RENDERPATH_GL20:
1987 qglBegin(GL_LINE_LOOP);
1988 for (num = 0;num < mesh->num_vertices;num++)
1990 if (mesh->data_color4f)
1991 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]);
1992 qglVertex2f(mesh->data_vertex3f[num*3+0], mesh->data_vertex3f[num*3+1]);
1998 case RENDERPATH_D3D9:
1999 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2001 case RENDERPATH_D3D10:
2002 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2004 case RENDERPATH_D3D11:
2005 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2007 case RENDERPATH_SOFT:
2008 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2010 case RENDERPATH_GLES1:
2011 case RENDERPATH_GLES2:
2012 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2017 //[515]: this is old, delete
2018 void DrawQ_Line (float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags)
2020 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
2021 if(!r_draw2d.integer && !r_draw2d_force)
2024 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
2026 switch(vid.renderpath)
2028 case RENDERPATH_GL11:
2029 case RENDERPATH_GL13:
2030 case RENDERPATH_GL20:
2034 //qglLineWidth(width);CHECKGLERROR
2036 GL_Color(r,g,b,alpha);
2039 qglVertex2f(x1, y1);
2040 qglVertex2f(x2, y2);
2045 case RENDERPATH_D3D9:
2046 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2048 case RENDERPATH_D3D10:
2049 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2051 case RENDERPATH_D3D11:
2052 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2054 case RENDERPATH_SOFT:
2055 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2057 case RENDERPATH_GLES1:
2058 case RENDERPATH_GLES2:
2059 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2064 void DrawQ_Lines (float width, int numlines, const float *vertex3f, const float *color4f, int flags)
2067 qboolean hasalpha = false;
2068 for (i = 0;i < numlines*2;i++)
2069 if (color4f[i*4+3] < 1.0f)
2072 _DrawQ_SetupAndProcessDrawFlag(flags, NULL, hasalpha ? 0.5f : 1.0f);
2074 if(!r_draw2d.integer && !r_draw2d_force)
2077 switch(vid.renderpath)
2079 case RENDERPATH_GL11:
2080 case RENDERPATH_GL13:
2081 case RENDERPATH_GL20:
2084 R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
2086 //qglLineWidth(width);CHECKGLERROR
2089 R_Mesh_PrepareVertices_Generic_Arrays(numlines*2, vertex3f, color4f, NULL);
2090 qglDrawArrays(GL_LINES, 0, numlines*2);
2093 case RENDERPATH_D3D9:
2094 //Con_DPrintf("FIXME D3D9 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2096 case RENDERPATH_D3D10:
2097 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2099 case RENDERPATH_D3D11:
2100 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2102 case RENDERPATH_SOFT:
2103 //Con_DPrintf("FIXME SOFT %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2105 case RENDERPATH_GLES1:
2106 case RENDERPATH_GLES2:
2107 //Con_DPrintf("FIXME GLES2 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2112 void DrawQ_SetClipArea(float x, float y, float width, float height)
2117 // We have to convert the con coords into real coords
2118 // OGL uses top to bottom
2119 ix = (int)(0.5 + x * ((float)vid.width / vid_conwidth.integer));
2120 iy = (int)(0.5 + y * ((float) vid.height / vid_conheight.integer));
2121 iw = (int)(0.5 + (x+width) * ((float)vid.width / vid_conwidth.integer)) - ix;
2122 ih = (int)(0.5 + (y+height) * ((float) vid.height / vid_conheight.integer)) - iy;
2123 switch(vid.renderpath)
2125 case RENDERPATH_GL11:
2126 case RENDERPATH_GL13:
2127 case RENDERPATH_GL20:
2128 case RENDERPATH_GLES1:
2129 case RENDERPATH_GLES2:
2130 case RENDERPATH_SOFT:
2131 GL_Scissor(ix, vid.height - iy - ih, iw, ih);
2133 case RENDERPATH_D3D9:
2134 GL_Scissor(ix, iy, iw, ih);
2136 case RENDERPATH_D3D10:
2137 Con_DPrintf("FIXME D3D10 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2139 case RENDERPATH_D3D11:
2140 Con_DPrintf("FIXME D3D11 %s:%i %s\n", __FILE__, __LINE__, __FUNCTION__);
2144 GL_ScissorTest(true);
2147 void DrawQ_ResetClipArea(void)
2150 GL_ScissorTest(false);
2153 void DrawQ_Finish(void)
2155 r_refdef.draw2dstage = 0;
2158 void DrawQ_RecalcView(void)
2160 if(r_refdef.draw2dstage)
2161 r_refdef.draw2dstage = -1; // next draw call will set viewport etc. again
2164 static float blendvertex3f[9] = {-5000, -5000, 10, 10000, -5000, 10, -5000, 10000, 10};
2165 void R_DrawGamma(void)
2168 switch(vid.renderpath)
2170 case RENDERPATH_GL20:
2171 case RENDERPATH_D3D9:
2172 case RENDERPATH_D3D10:
2173 case RENDERPATH_D3D11:
2174 case RENDERPATH_GLES2:
2175 if (vid_usinghwgamma || v_glslgamma.integer)
2178 case RENDERPATH_GL11:
2179 case RENDERPATH_GL13:
2180 if (vid_usinghwgamma)
2183 case RENDERPATH_GLES1:
2184 case RENDERPATH_SOFT:
2187 // all the blends ignore depth
2188 // R_Mesh_ResetTextureState();
2189 R_SetupShader_Generic_NoTexture(true, true);
2191 GL_DepthRange(0, 1);
2192 GL_PolygonOffset(0, 0);
2193 GL_DepthTest(false);
2195 // interpretation of brightness and contrast:
2196 // color range := brightness .. (brightness + contrast)
2197 // i.e. "c *= contrast; c += brightness"
2198 // plausible values for brightness thus range from -contrast to 1
2200 // apply pre-brightness (subtractive brightness, for where contrast was >= 1)
2201 if (vid.support.ext_blend_subtract)
2203 if (v_color_enable.integer)
2205 c[0] = -v_color_black_r.value / v_color_white_r.value;
2206 c[1] = -v_color_black_g.value / v_color_white_g.value;
2207 c[2] = -v_color_black_b.value / v_color_white_b.value;
2210 c[0] = c[1] = c[2] = -v_brightness.value / v_contrast.value;
2211 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2213 // need SUBTRACTIVE blending to do this!
2214 GL_BlendEquationSubtract(true);
2215 GL_BlendFunc(GL_ONE, GL_ONE);
2216 GL_Color(c[0], c[1], c[2], 1);
2217 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2218 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2219 GL_BlendEquationSubtract(false);
2224 if (v_color_enable.integer)
2226 c[0] = v_color_white_r.value;
2227 c[1] = v_color_white_g.value;
2228 c[2] = v_color_white_b.value;
2231 c[0] = c[1] = c[2] = v_contrast.value;
2232 if (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2234 GL_BlendFunc(GL_DST_COLOR, GL_ONE);
2235 while (c[0] >= 1.003f || c[1] >= 1.003f || c[2] >= 1.003f)
2238 cc[0] = bound(0, c[0] - 1, 1);
2239 cc[1] = bound(0, c[1] - 1, 1);
2240 cc[2] = bound(0, c[2] - 1, 1);
2241 GL_Color(cc[0], cc[1], cc[2], 1);
2242 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2243 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2249 if (c[0] <= 0.997f || c[1] <= 0.997f || c[2] <= 0.997f)
2251 GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
2252 GL_Color(c[0], c[1], c[2], 1);
2253 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2254 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
2257 // apply post-brightness (additive brightness, for where contrast was <= 1)
2258 if (v_color_enable.integer)
2260 c[0] = v_color_black_r.value;
2261 c[1] = v_color_black_g.value;
2262 c[2] = v_color_black_b.value;
2265 c[0] = c[1] = c[2] = v_brightness.value;
2266 if (c[0] >= 0.01f || c[1] >= 0.01f || c[2] >= 0.01f)
2268 GL_BlendFunc(GL_ONE, GL_ONE);
2269 GL_Color(c[0], c[1], c[2], 1);
2270 R_Mesh_PrepareVertices_Generic_Arrays(3, blendvertex3f, NULL, NULL);
2271 R_Mesh_Draw(0, 3, 0, 1, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);