PageRenderTime 53ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/gl_draw.c

https://gitlab.com/xonotic/darkplaces
C | 2156 lines | 1789 code | 188 blank | 179 comment | 431 complexity | cee5543a0b16238b6987aaf56b20ae1c MD5 | raw file
Possible License(s): GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. Copyright (C) 1996-1997 Id Software, Inc.
  3. This program is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU General Public License
  5. as published by the Free Software Foundation; either version 2
  6. of the License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. See the GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  14. */
  15. #include "quakedef.h"
  16. #include "image.h"
  17. #include "wad.h"
  18. #include "cl_video.h"
  19. #include "cl_dyntexture.h"
  20. #include "ft2.h"
  21. #include "ft2_fontdefs.h"
  22. dp_fonts_t dp_fonts;
  23. static mempool_t *fonts_mempool = NULL;
  24. 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)"};
  25. 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)"};
  26. 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)"};
  27. cvar_t r_font_postprocess_blur = {CVAR_SAVE, "r_font_postprocess_blur", "0", "font blur amount"};
  28. cvar_t r_font_postprocess_outline = {CVAR_SAVE, "r_font_postprocess_outline", "0", "font outline amount"};
  29. cvar_t r_font_postprocess_shadow_x = {CVAR_SAVE, "r_font_postprocess_shadow_x", "0", "font shadow X shift amount, applied during outlining"};
  30. cvar_t r_font_postprocess_shadow_y = {CVAR_SAVE, "r_font_postprocess_shadow_y", "0", "font shadow Y shift amount, applied during outlining"};
  31. cvar_t r_font_postprocess_shadow_z = {CVAR_SAVE, "r_font_postprocess_shadow_z", "0", "font shadow Z shift amount, applied during blurring"};
  32. cvar_t r_font_hinting = {CVAR_SAVE, "r_font_hinting", "3", "0 = no hinting, 1 = light autohinting, 2 = full autohinting, 3 = full hinting"};
  33. cvar_t r_font_antialias = {CVAR_SAVE, "r_font_antialias", "1", "0 = monochrome, 1 = grey" /* , 2 = rgb, 3 = bgr" */};
  34. cvar_t r_nearest_2d = {CVAR_SAVE, "r_nearest_2d", "0", "use nearest filtering on all 2d textures (including conchars)"};
  35. cvar_t r_nearest_conchars = {CVAR_SAVE, "r_nearest_conchars", "0", "use nearest filtering on conchars texture"};
  36. //=============================================================================
  37. /* Support Routines */
  38. #define FONT_FILESIZE 13468
  39. static cachepic_t *cachepichash[CACHEPICHASHSIZE];
  40. static cachepic_t cachepics[MAX_CACHED_PICS];
  41. static int numcachepics;
  42. rtexturepool_t *drawtexturepool;
  43. static const unsigned char concharimage[FONT_FILESIZE] =
  44. {
  45. #include "lhfont.h"
  46. };
  47. static rtexture_t *draw_generateconchars(void)
  48. {
  49. int i;
  50. unsigned char *data;
  51. double random;
  52. rtexture_t *tex;
  53. data = LoadTGA_BGRA (concharimage, FONT_FILESIZE, NULL);
  54. // Gold numbers
  55. for (i = 0;i < 8192;i++)
  56. {
  57. random = lhrandom (0.0,1.0);
  58. data[i*4+3] = data[i*4+0];
  59. data[i*4+2] = 83 + (unsigned char)(random * 64);
  60. data[i*4+1] = 71 + (unsigned char)(random * 32);
  61. data[i*4+0] = 23 + (unsigned char)(random * 16);
  62. }
  63. // White chars
  64. for (i = 8192;i < 32768;i++)
  65. {
  66. random = lhrandom (0.0,1.0);
  67. data[i*4+3] = data[i*4+0];
  68. data[i*4+2] = 95 + (unsigned char)(random * 64);
  69. data[i*4+1] = 95 + (unsigned char)(random * 64);
  70. data[i*4+0] = 95 + (unsigned char)(random * 64);
  71. }
  72. // Gold numbers
  73. for (i = 32768;i < 40960;i++)
  74. {
  75. random = lhrandom (0.0,1.0);
  76. data[i*4+3] = data[i*4+0];
  77. data[i*4+2] = 83 + (unsigned char)(random * 64);
  78. data[i*4+1] = 71 + (unsigned char)(random * 32);
  79. data[i*4+0] = 23 + (unsigned char)(random * 16);
  80. }
  81. // Red chars
  82. for (i = 40960;i < 65536;i++)
  83. {
  84. random = lhrandom (0.0,1.0);
  85. data[i*4+3] = data[i*4+0];
  86. data[i*4+2] = 96 + (unsigned char)(random * 64);
  87. data[i*4+1] = 43 + (unsigned char)(random * 32);
  88. data[i*4+0] = 27 + (unsigned char)(random * 32);
  89. }
  90. #if 0
  91. Image_WriteTGABGRA ("gfx/generated_conchars.tga", 256, 256, data);
  92. #endif
  93. tex = R_LoadTexture2D(drawtexturepool, "conchars", 256, 256, data, TEXTYPE_BGRA, TEXF_ALPHA | (r_nearest_conchars.integer ? TEXF_FORCENEAREST : 0), -1, NULL);
  94. Mem_Free(data);
  95. return tex;
  96. }
  97. static rtexture_t *draw_generateditherpattern(void)
  98. {
  99. int x, y;
  100. unsigned char pixels[8][8];
  101. for (y = 0;y < 8;y++)
  102. for (x = 0;x < 8;x++)
  103. pixels[y][x] = ((x^y) & 4) ? 254 : 0;
  104. return R_LoadTexture2D(drawtexturepool, "ditherpattern", 8, 8, pixels[0], TEXTYPE_PALETTE, TEXF_FORCENEAREST, -1, palette_bgra_transparent);
  105. }
  106. typedef struct embeddedpic_s
  107. {
  108. const char *name;
  109. int width;
  110. int height;
  111. const char *pixels;
  112. }
  113. embeddedpic_t;
  114. static const embeddedpic_t embeddedpics[] =
  115. {
  116. {
  117. "gfx/prydoncursor001", 16, 16,
  118. "477777774......."
  119. "77.....6........"
  120. "7.....6........."
  121. "7....6.........."
  122. "7.....6........."
  123. "7..6...6........"
  124. "7.6.6...6......."
  125. "76...6...6......"
  126. "4.....6.6......."
  127. ".......6........"
  128. "................"
  129. "................"
  130. "................"
  131. "................"
  132. "................"
  133. "................"
  134. },
  135. {
  136. "ui/mousepointer", 16, 16,
  137. "477777774......."
  138. "77.....6........"
  139. "7.....6........."
  140. "7....6.........."
  141. "7.....6........."
  142. "7..6...6........"
  143. "7.6.6...6......."
  144. "76...6...6......"
  145. "4.....6.6......."
  146. ".......6........"
  147. "................"
  148. "................"
  149. "................"
  150. "................"
  151. "................"
  152. "................"
  153. },
  154. {
  155. "gfx/crosshair1", 16, 16,
  156. "................"
  157. "................"
  158. "................"
  159. "...33......33..."
  160. "...355....553..."
  161. "....577..775...."
  162. ".....77..77....."
  163. "................"
  164. "................"
  165. ".....77..77....."
  166. "....577..775...."
  167. "...355....553..."
  168. "...33......33..."
  169. "................"
  170. "................"
  171. "................"
  172. },
  173. {
  174. "gfx/crosshair2", 16, 16,
  175. "................"
  176. "................"
  177. "................"
  178. "...3........3..."
  179. "....5......5...."
  180. ".....7....7....."
  181. "......7..7......"
  182. "................"
  183. "................"
  184. "......7..7......"
  185. ".....7....7....."
  186. "....5......5...."
  187. "...3........3..."
  188. "................"
  189. "................"
  190. "................"
  191. },
  192. {
  193. "gfx/crosshair3", 16, 16,
  194. "................"
  195. ".......77......."
  196. ".......77......."
  197. "................"
  198. "................"
  199. ".......44......."
  200. ".......44......."
  201. ".77..44..44..77."
  202. ".77..44..44..77."
  203. ".......44......."
  204. ".......44......."
  205. "................"
  206. "................"
  207. ".......77......."
  208. ".......77......."
  209. "................"
  210. },
  211. {
  212. "gfx/crosshair4", 16, 16,
  213. "................"
  214. "................"
  215. "................"
  216. "................"
  217. "................"
  218. "................"
  219. "................"
  220. "................"
  221. "........7777777."
  222. "........752....."
  223. "........72......"
  224. "........7......."
  225. "........7......."
  226. "........7......."
  227. "........7......."
  228. "................"
  229. },
  230. {
  231. "gfx/crosshair5", 8, 8,
  232. "........"
  233. "........"
  234. "....7..."
  235. "........"
  236. "..7.7.7."
  237. "........"
  238. "....7..."
  239. "........"
  240. },
  241. {
  242. "gfx/crosshair6", 2, 2,
  243. "77"
  244. "77"
  245. },
  246. {
  247. "gfx/crosshair7", 16, 16,
  248. "................"
  249. ".3............3."
  250. "..5...2332...5.."
  251. "...7.3....3.7..."
  252. "....7......7...."
  253. "...3.7....7.3..."
  254. "..2...7..7...2.."
  255. "..3..........3.."
  256. "..3..........3.."
  257. "..2...7..7...2.."
  258. "...3.7....7.3..."
  259. "....7......7...."
  260. "...7.3....3.7..."
  261. "..5...2332...5.."
  262. ".3............3."
  263. "................"
  264. },
  265. {NULL, 0, 0, NULL}
  266. };
  267. static rtexture_t *draw_generatepic(const char *name, qboolean quiet)
  268. {
  269. const embeddedpic_t *p;
  270. for (p = embeddedpics;p->name;p++)
  271. if (!strcmp(name, p->name))
  272. return R_LoadTexture2D(drawtexturepool, p->name, p->width, p->height, (const unsigned char *)p->pixels, TEXTYPE_PALETTE, TEXF_ALPHA, -1, palette_bgra_embeddedpic);
  273. if (!strcmp(name, "gfx/conchars"))
  274. return draw_generateconchars();
  275. if (!strcmp(name, "gfx/colorcontrol/ditherpattern"))
  276. return draw_generateditherpattern();
  277. if (!quiet)
  278. Con_DPrintf("Draw_CachePic: failed to load %s\n", name);
  279. return r_texture_notexture;
  280. }
  281. int draw_frame = 1;
  282. /*
  283. ================
  284. Draw_CachePic
  285. ================
  286. */
  287. // FIXME: move this to client somehow
  288. cachepic_t *Draw_CachePic_Flags(const char *path, unsigned int cachepicflags)
  289. {
  290. int crc, hashkey;
  291. unsigned char *pixels = NULL;
  292. cachepic_t *pic;
  293. fs_offset_t lmpsize;
  294. unsigned char *lmpdata;
  295. char lmpname[MAX_QPATH];
  296. int texflags;
  297. int j;
  298. qboolean ddshasalpha;
  299. float ddsavgcolor[4];
  300. qboolean loaded = false;
  301. char vabuf[1024];
  302. texflags = TEXF_ALPHA;
  303. if (!(cachepicflags & CACHEPICFLAG_NOCLAMP))
  304. texflags |= TEXF_CLAMP;
  305. if (cachepicflags & CACHEPICFLAG_MIPMAP)
  306. texflags |= TEXF_MIPMAP;
  307. if (!(cachepicflags & CACHEPICFLAG_NOCOMPRESSION) && gl_texturecompression_2d.integer && gl_texturecompression.integer)
  308. texflags |= TEXF_COMPRESS;
  309. if ((cachepicflags & CACHEPICFLAG_NEAREST) || r_nearest_2d.integer)
  310. texflags |= TEXF_FORCENEAREST;
  311. // check whether the picture has already been cached
  312. crc = CRC_Block((unsigned char *)path, strlen(path));
  313. hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
  314. for (pic = cachepichash[hashkey];pic;pic = pic->chain)
  315. {
  316. if (!strcmp (path, pic->name))
  317. {
  318. // if it was created (or replaced) by Draw_NewPic, just return it
  319. if(pic->flags & CACHEPICFLAG_NEWPIC)
  320. return pic;
  321. if (!((pic->texflags ^ texflags) & ~(TEXF_COMPRESS | TEXF_MIPMAP))) // ignore TEXF_COMPRESS when comparing, because fallback pics remove the flag, and ignore TEXF_MIPMAP because QC specifies that
  322. {
  323. if(!(cachepicflags & CACHEPICFLAG_NOTPERSISTENT))
  324. {
  325. if(pic->tex)
  326. pic->autoload = false; // persist it
  327. else
  328. goto reload; // load it below, and then persist
  329. }
  330. return pic;
  331. }
  332. }
  333. }
  334. if (numcachepics == MAX_CACHED_PICS)
  335. {
  336. Con_Printf ("Draw_CachePic: numcachepics == MAX_CACHED_PICS\n");
  337. // FIXME: support NULL in callers?
  338. return cachepics; // return the first one
  339. }
  340. pic = cachepics + (numcachepics++);
  341. memset(pic, 0, sizeof(*pic));
  342. strlcpy (pic->name, path, sizeof(pic->name));
  343. // link into list
  344. pic->chain = cachepichash[hashkey];
  345. cachepichash[hashkey] = pic;
  346. reload:
  347. // TODO why does this crash?
  348. if(pic->allow_free_tex && pic->tex)
  349. R_PurgeTexture(pic->tex);
  350. // check whether it is an dynamic texture (if so, we can directly use its texture handler)
  351. pic->flags = cachepicflags;
  352. pic->tex = CL_GetDynTexture( path );
  353. // if so, set the width/height, too
  354. if( pic->tex ) {
  355. pic->allow_free_tex = false;
  356. pic->width = R_TextureWidth(pic->tex);
  357. pic->height = R_TextureHeight(pic->tex);
  358. // we're done now (early-out)
  359. return pic;
  360. }
  361. pic->allow_free_tex = true;
  362. pic->hasalpha = true; // assume alpha unless we know it has none
  363. pic->texflags = texflags;
  364. pic->autoload = (cachepicflags & CACHEPICFLAG_NOTPERSISTENT);
  365. pic->lastusedframe = draw_frame;
  366. // load a high quality image from disk if possible
  367. 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, false)))
  368. {
  369. // note this loads even if autoload is true, otherwise we can't get the width/height
  370. loaded = true;
  371. pic->hasalpha = ddshasalpha;
  372. pic->width = R_TextureWidth(pic->tex);
  373. pic->height = R_TextureHeight(pic->tex);
  374. }
  375. if (!loaded && ((pixels = loadimagepixelsbgra(pic->name, false, true, false, NULL)) || (!strncmp(pic->name, "gfx/", 4) && (pixels = loadimagepixelsbgra(pic->name+4, false, true, false, NULL)))))
  376. {
  377. loaded = true;
  378. pic->hasalpha = false;
  379. if (pic->texflags & TEXF_ALPHA)
  380. {
  381. for (j = 3;j < image_width * image_height * 4;j += 4)
  382. {
  383. if (pixels[j] < 255)
  384. {
  385. pic->hasalpha = true;
  386. break;
  387. }
  388. }
  389. }
  390. pic->width = image_width;
  391. pic->height = image_height;
  392. if (!pic->autoload)
  393. {
  394. 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);
  395. #ifndef USE_GLES2
  396. if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
  397. R_SaveTextureDDSFile(pic->tex, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
  398. #endif
  399. }
  400. }
  401. if (!loaded)
  402. {
  403. pic->autoload = false;
  404. // never compress the fallback images
  405. pic->texflags &= ~TEXF_COMPRESS;
  406. }
  407. // now read the low quality version (wad or lmp file), and take the pic
  408. // size from that even if we don't upload the texture, this way the pics
  409. // show up the right size in the menu even if they were replaced with
  410. // higher or lower resolution versions
  411. dpsnprintf(lmpname, sizeof(lmpname), "%s.lmp", pic->name);
  412. if ((!strncmp(pic->name, "gfx/", 4) || (gamemode == GAME_BLOODOMNICIDE && !strncmp(pic->name, "locale/", 6))) && (lmpdata = FS_LoadFile(lmpname, tempmempool, false, &lmpsize)))
  413. {
  414. if (developer_loading.integer)
  415. Con_Printf("loading lump \"%s\"\n", pic->name);
  416. if (lmpsize >= 9)
  417. {
  418. pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
  419. pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
  420. // if no high quality replacement image was found, upload the original low quality texture
  421. if (!loaded)
  422. {
  423. loaded = true;
  424. 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);
  425. }
  426. }
  427. Mem_Free(lmpdata);
  428. }
  429. else if ((lmpdata = W_GetLumpName (pic->name + 4)))
  430. {
  431. if (developer_loading.integer)
  432. Con_Printf("loading gfx.wad lump \"%s\"\n", pic->name + 4);
  433. if (!strcmp(pic->name, "gfx/conchars"))
  434. {
  435. // conchars is a raw image and with color 0 as transparent instead of 255
  436. pic->width = 128;
  437. pic->height = 128;
  438. // if no high quality replacement image was found, upload the original low quality texture
  439. if (!loaded)
  440. {
  441. loaded = true;
  442. pic->tex = R_LoadTexture2D(drawtexturepool, pic->name, 128, 128, lmpdata, vid.sRGB2D != 0 ? TEXTYPE_SRGB_PALETTE : TEXTYPE_PALETTE, pic->texflags, -1, palette_bgra_font);
  443. }
  444. }
  445. else
  446. {
  447. pic->width = lmpdata[0] + lmpdata[1] * 256 + lmpdata[2] * 65536 + lmpdata[3] * 16777216;
  448. pic->height = lmpdata[4] + lmpdata[5] * 256 + lmpdata[6] * 65536 + lmpdata[7] * 16777216;
  449. // if no high quality replacement image was found, upload the original low quality texture
  450. if (!loaded)
  451. {
  452. loaded = true;
  453. 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);
  454. }
  455. }
  456. }
  457. if (pixels)
  458. {
  459. Mem_Free(pixels);
  460. pixels = NULL;
  461. }
  462. if (!loaded)
  463. {
  464. // if it's not found on disk, generate an image
  465. pic->tex = draw_generatepic(pic->name, (cachepicflags & CACHEPICFLAG_QUIET) != 0);
  466. pic->width = R_TextureWidth(pic->tex);
  467. pic->height = R_TextureHeight(pic->tex);
  468. pic->allow_free_tex = (pic->tex != r_texture_notexture);
  469. }
  470. return pic;
  471. }
  472. cachepic_t *Draw_CachePic (const char *path)
  473. {
  474. return Draw_CachePic_Flags (path, 0); // default to persistent!
  475. }
  476. rtexture_t *Draw_GetPicTexture(cachepic_t *pic)
  477. {
  478. char vabuf[1024];
  479. if (pic->autoload && !pic->tex)
  480. {
  481. if (pic->tex == NULL && r_texture_dds_load.integer != 0)
  482. {
  483. qboolean ddshasalpha;
  484. float ddsavgcolor[4];
  485. pic->tex = R_LoadTextureDDSFile(drawtexturepool, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), vid.sRGB2D, pic->texflags, &ddshasalpha, ddsavgcolor, 0, false);
  486. }
  487. if (pic->tex == NULL)
  488. {
  489. pic->tex = loadtextureimage(drawtexturepool, pic->name, false, pic->texflags, true, vid.sRGB2D);
  490. #ifndef USE_GLES2
  491. if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
  492. R_SaveTextureDDSFile(pic->tex, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
  493. #endif
  494. }
  495. if (pic->tex == NULL && !strncmp(pic->name, "gfx/", 4))
  496. {
  497. pic->tex = loadtextureimage(drawtexturepool, pic->name+4, false, pic->texflags, true, vid.sRGB2D);
  498. #ifndef USE_GLES2
  499. if (r_texture_dds_save.integer && qglGetCompressedTexImageARB && pic->tex)
  500. R_SaveTextureDDSFile(pic->tex, va(vabuf, sizeof(vabuf), "dds/%s.dds", pic->name), r_texture_dds_save.integer < 2, pic->hasalpha);
  501. #endif
  502. }
  503. if (pic->tex == NULL)
  504. pic->tex = draw_generatepic(pic->name, true);
  505. }
  506. pic->lastusedframe = draw_frame;
  507. return pic->tex;
  508. }
  509. void Draw_Frame(void)
  510. {
  511. int i;
  512. cachepic_t *pic;
  513. static double nextpurgetime;
  514. if (nextpurgetime > realtime)
  515. return;
  516. nextpurgetime = realtime + 0.05;
  517. for (i = 0, pic = cachepics;i < numcachepics;i++, pic++)
  518. {
  519. if (pic->autoload && pic->tex && pic->lastusedframe < draw_frame)
  520. {
  521. R_FreeTexture(pic->tex);
  522. pic->tex = NULL;
  523. }
  524. }
  525. draw_frame++;
  526. }
  527. cachepic_t *Draw_NewPic(const char *picname, int width, int height, int alpha, unsigned char *pixels_bgra)
  528. {
  529. int crc, hashkey;
  530. cachepic_t *pic;
  531. crc = CRC_Block((unsigned char *)picname, strlen(picname));
  532. hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
  533. for (pic = cachepichash[hashkey];pic;pic = pic->chain)
  534. if (!strcmp (picname, pic->name))
  535. break;
  536. if (pic)
  537. {
  538. if (pic->flags == CACHEPICFLAG_NEWPIC && pic->tex && pic->width == width && pic->height == height)
  539. {
  540. R_UpdateTexture(pic->tex, pixels_bgra, 0, 0, 0, width, height, 1);
  541. return pic;
  542. }
  543. }
  544. else
  545. {
  546. if (numcachepics == MAX_CACHED_PICS)
  547. {
  548. Con_Printf ("Draw_NewPic: numcachepics == MAX_CACHED_PICS\n");
  549. // FIXME: support NULL in callers?
  550. return cachepics; // return the first one
  551. }
  552. pic = cachepics + (numcachepics++);
  553. memset(pic, 0, sizeof(*pic));
  554. strlcpy (pic->name, picname, sizeof(pic->name));
  555. // link into list
  556. pic->chain = cachepichash[hashkey];
  557. cachepichash[hashkey] = pic;
  558. }
  559. pic->flags = CACHEPICFLAG_NEWPIC; // disable texflags checks in Draw_CachePic
  560. pic->width = width;
  561. pic->height = height;
  562. if (pic->allow_free_tex && pic->tex)
  563. R_FreeTexture(pic->tex);
  564. pic->tex = R_LoadTexture2D(drawtexturepool, picname, width, height, pixels_bgra, TEXTYPE_BGRA, (alpha ? TEXF_ALPHA : 0), -1, NULL);
  565. return pic;
  566. }
  567. void Draw_FreePic(const char *picname)
  568. {
  569. int crc;
  570. int hashkey;
  571. cachepic_t *pic;
  572. // this doesn't really free the pic, but does free it's texture
  573. crc = CRC_Block((unsigned char *)picname, strlen(picname));
  574. hashkey = ((crc >> 8) ^ crc) % CACHEPICHASHSIZE;
  575. for (pic = cachepichash[hashkey];pic;pic = pic->chain)
  576. {
  577. if (!strcmp (picname, pic->name) && pic->tex)
  578. {
  579. R_FreeTexture(pic->tex);
  580. pic->tex = NULL;
  581. pic->width = 0;
  582. pic->height = 0;
  583. return;
  584. }
  585. }
  586. }
  587. static float snap_to_pixel_x(float x, float roundUpAt);
  588. extern int con_linewidth; // to force rewrapping
  589. void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset)
  590. {
  591. int i, ch;
  592. float maxwidth;
  593. char widthfile[MAX_QPATH];
  594. char *widthbuf;
  595. fs_offset_t widthbufsize;
  596. if(override || !fnt->texpath[0])
  597. {
  598. strlcpy(fnt->texpath, name, sizeof(fnt->texpath));
  599. // load the cvars when the font is FIRST loader
  600. fnt->settings.scale = scale;
  601. fnt->settings.voffset = voffset;
  602. fnt->settings.antialias = r_font_antialias.integer;
  603. fnt->settings.hinting = r_font_hinting.integer;
  604. fnt->settings.outline = r_font_postprocess_outline.value;
  605. fnt->settings.blur = r_font_postprocess_blur.value;
  606. fnt->settings.shadowx = r_font_postprocess_shadow_x.value;
  607. fnt->settings.shadowy = r_font_postprocess_shadow_y.value;
  608. fnt->settings.shadowz = r_font_postprocess_shadow_z.value;
  609. }
  610. // fix bad scale
  611. if (fnt->settings.scale <= 0)
  612. fnt->settings.scale = 1;
  613. if(drawtexturepool == NULL)
  614. return; // before gl_draw_start, so will be loaded later
  615. if(fnt->ft2)
  616. {
  617. // clear freetype font
  618. Font_UnloadFont(fnt->ft2);
  619. Mem_Free(fnt->ft2);
  620. fnt->ft2 = NULL;
  621. }
  622. if(fnt->req_face != -1)
  623. {
  624. if(!Font_LoadFont(fnt->texpath, fnt))
  625. Con_DPrintf("Failed to load font-file for '%s', it will not support as many characters.\n", fnt->texpath);
  626. }
  627. fnt->tex = Draw_CachePic_Flags(fnt->texpath, CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION | (r_nearest_conchars.integer ? CACHEPICFLAG_NEAREST : 0))->tex;
  628. if(fnt->tex == r_texture_notexture)
  629. {
  630. for (i = 0; i < MAX_FONT_FALLBACKS; ++i)
  631. {
  632. if (!fnt->fallbacks[i][0])
  633. break;
  634. fnt->tex = Draw_CachePic_Flags(fnt->fallbacks[i], CACHEPICFLAG_QUIET | CACHEPICFLAG_NOCOMPRESSION | (r_nearest_conchars.integer ? CACHEPICFLAG_NEAREST : 0))->tex;
  635. if(fnt->tex != r_texture_notexture)
  636. break;
  637. }
  638. if(fnt->tex == r_texture_notexture)
  639. {
  640. fnt->tex = Draw_CachePic_Flags("gfx/conchars", CACHEPICFLAG_NOCOMPRESSION | (r_nearest_conchars.integer ? CACHEPICFLAG_NEAREST : 0))->tex;
  641. strlcpy(widthfile, "gfx/conchars.width", sizeof(widthfile));
  642. }
  643. else
  644. dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->fallbacks[i]);
  645. }
  646. else
  647. dpsnprintf(widthfile, sizeof(widthfile), "%s.width", fnt->texpath);
  648. // unspecified width == 1 (base width)
  649. for(ch = 0; ch < 256; ++ch)
  650. fnt->width_of[ch] = 1;
  651. // FIXME load "name.width", if it fails, fill all with 1
  652. if((widthbuf = (char *) FS_LoadFile(widthfile, tempmempool, true, &widthbufsize)))
  653. {
  654. float extraspacing = 0;
  655. const char *p = widthbuf;
  656. ch = 0;
  657. while(ch < 256)
  658. {
  659. if(!COM_ParseToken_Simple(&p, false, false, true))
  660. return;
  661. switch(*com_token)
  662. {
  663. case '0':
  664. case '1':
  665. case '2':
  666. case '3':
  667. case '4':
  668. case '5':
  669. case '6':
  670. case '7':
  671. case '8':
  672. case '9':
  673. case '+':
  674. case '-':
  675. case '.':
  676. fnt->width_of[ch] = atof(com_token) + extraspacing;
  677. ch++;
  678. break;
  679. default:
  680. if(!strcmp(com_token, "extraspacing"))
  681. {
  682. if(!COM_ParseToken_Simple(&p, false, false, true))
  683. return;
  684. extraspacing = atof(com_token);
  685. }
  686. else if(!strcmp(com_token, "scale"))
  687. {
  688. if(!COM_ParseToken_Simple(&p, false, false, true))
  689. return;
  690. fnt->settings.scale = atof(com_token);
  691. }
  692. else
  693. {
  694. Con_Printf("Warning: skipped unknown font property %s\n", com_token);
  695. if(!COM_ParseToken_Simple(&p, false, false, true))
  696. return;
  697. }
  698. break;
  699. }
  700. }
  701. Mem_Free(widthbuf);
  702. }
  703. if(fnt->ft2)
  704. {
  705. for (i = 0; i < MAX_FONT_SIZES; ++i)
  706. {
  707. ft2_font_map_t *map = Font_MapForIndex(fnt->ft2, i);
  708. if (!map)
  709. break;
  710. for(ch = 0; ch < 256; ++ch)
  711. map->width_of[ch] = Font_SnapTo(fnt->width_of[ch], 1/map->size);
  712. }
  713. }
  714. maxwidth = fnt->width_of[0];
  715. for(i = 1; i < 256; ++i)
  716. maxwidth = max(maxwidth, fnt->width_of[i]);
  717. fnt->maxwidth = maxwidth;
  718. // fix up maxwidth for overlap
  719. fnt->maxwidth *= fnt->settings.scale;
  720. if(fnt == FONT_CONSOLE)
  721. con_linewidth = -1; // rewrap console in next frame
  722. }
  723. extern cvar_t developer_font;
  724. dp_font_t *FindFont(const char *title, qboolean allocate_new)
  725. {
  726. int i, oldsize;
  727. // find font
  728. for(i = 0; i < dp_fonts.maxsize; ++i)
  729. if(!strcmp(dp_fonts.f[i].title, title))
  730. return &dp_fonts.f[i];
  731. // if not found - try allocate
  732. if (allocate_new)
  733. {
  734. // find any font with empty title
  735. for(i = 0; i < dp_fonts.maxsize; ++i)
  736. {
  737. if(!strcmp(dp_fonts.f[i].title, ""))
  738. {
  739. strlcpy(dp_fonts.f[i].title, title, sizeof(dp_fonts.f[i].title));
  740. return &dp_fonts.f[i];
  741. }
  742. }
  743. // if no any 'free' fonts - expand buffer
  744. oldsize = dp_fonts.maxsize;
  745. dp_fonts.maxsize = dp_fonts.maxsize + FONTS_EXPAND;
  746. if (developer_font.integer)
  747. Con_Printf("FindFont: enlarging fonts buffer (%i -> %i)\n", oldsize, dp_fonts.maxsize);
  748. dp_fonts.f = (dp_font_t *)Mem_Realloc(fonts_mempool, dp_fonts.f, sizeof(dp_font_t) * dp_fonts.maxsize);
  749. // relink ft2 structures
  750. for(i = 0; i < oldsize; ++i)
  751. if (dp_fonts.f[i].ft2)
  752. dp_fonts.f[i].ft2->settings = &dp_fonts.f[i].settings;
  753. // register a font in first expanded slot
  754. strlcpy(dp_fonts.f[oldsize].title, title, sizeof(dp_fonts.f[oldsize].title));
  755. return &dp_fonts.f[oldsize];
  756. }
  757. return NULL;
  758. }
  759. static float snap_to_pixel_x(float x, float roundUpAt)
  760. {
  761. float pixelpos = x * vid.width / vid_conwidth.value;
  762. int snap = (int) pixelpos;
  763. if (pixelpos - snap >= roundUpAt) ++snap;
  764. return ((float)snap * vid_conwidth.value / vid.width);
  765. /*
  766. x = (int)(x * vid.width / vid_conwidth.value);
  767. x = (x * vid_conwidth.value / vid.width);
  768. return x;
  769. */
  770. }
  771. static float snap_to_pixel_y(float y, float roundUpAt)
  772. {
  773. float pixelpos = y * vid.height / vid_conheight.value;
  774. int snap = (int) pixelpos;
  775. if (pixelpos - snap > roundUpAt) ++snap;
  776. return ((float)snap * vid_conheight.value / vid.height);
  777. /*
  778. y = (int)(y * vid.height / vid_conheight.value);
  779. y = (y * vid_conheight.value / vid.height);
  780. return y;
  781. */
  782. }
  783. static void LoadFont_f(void)
  784. {
  785. dp_font_t *f;
  786. int i, sizes;
  787. const char *filelist, *c, *cm;
  788. float sz, scale, voffset;
  789. char mainfont[MAX_QPATH];
  790. if(Cmd_Argc() < 2)
  791. {
  792. Con_Printf("Available font commands:\n");
  793. for(i = 0; i < dp_fonts.maxsize; ++i)
  794. if (dp_fonts.f[i].title[0])
  795. Con_Printf(" loadfont %s gfx/tgafile[...] [custom switches] [sizes...]\n", dp_fonts.f[i].title);
  796. Con_Printf("A font can simply be gfx/tgafile, or alternatively you\n"
  797. "can specify multiple fonts and faces\n"
  798. "Like this: gfx/vera-sans:2,gfx/fallback:1\n"
  799. "to load face 2 of the font gfx/vera-sans and use face 1\n"
  800. "of gfx/fallback as fallback font.\n"
  801. "You can also specify a list of font sizes to load, like this:\n"
  802. "loadfont console gfx/conchars,gfx/fallback 8 12 16 24 32\n"
  803. "In many cases, 8 12 16 24 32 should be a good choice.\n"
  804. "custom switches:\n"
  805. " scale x : scale all characters by this amount when rendering (doesnt change line height)\n"
  806. " voffset x : offset all chars vertical when rendering, this is multiplied to character height\n"
  807. );
  808. return;
  809. }
  810. f = FindFont(Cmd_Argv(1), true);
  811. if(f == NULL)
  812. {
  813. Con_Printf("font function not found\n");
  814. return;
  815. }
  816. if(Cmd_Argc() < 3)
  817. filelist = "gfx/conchars";
  818. else
  819. filelist = Cmd_Argv(2);
  820. memset(f->fallbacks, 0, sizeof(f->fallbacks));
  821. memset(f->fallback_faces, 0, sizeof(f->fallback_faces));
  822. // first font is handled "normally"
  823. c = strchr(filelist, ':');
  824. cm = strchr(filelist, ',');
  825. if(c && (!cm || c < cm))
  826. f->req_face = atoi(c+1);
  827. else
  828. {
  829. f->req_face = 0;
  830. c = cm;
  831. }
  832. if(!c || (c - filelist) > MAX_QPATH)
  833. strlcpy(mainfont, filelist, sizeof(mainfont));
  834. else
  835. {
  836. memcpy(mainfont, filelist, c - filelist);
  837. mainfont[c - filelist] = 0;
  838. }
  839. for(i = 0; i < MAX_FONT_FALLBACKS; ++i)
  840. {
  841. c = strchr(filelist, ',');
  842. if(!c)
  843. break;
  844. filelist = c + 1;
  845. if(!*filelist)
  846. break;
  847. c = strchr(filelist, ':');
  848. cm = strchr(filelist, ',');
  849. if(c && (!cm || c < cm))
  850. f->fallback_faces[i] = atoi(c+1);
  851. else
  852. {
  853. f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index
  854. c = cm;
  855. }
  856. if(!c || (c-filelist) > MAX_QPATH)
  857. {
  858. strlcpy(f->fallbacks[i], filelist, sizeof(mainfont));
  859. }
  860. else
  861. {
  862. memcpy(f->fallbacks[i], filelist, c - filelist);
  863. f->fallbacks[i][c - filelist] = 0;
  864. }
  865. }
  866. // for now: by default load only one size: the default size
  867. f->req_sizes[0] = 0;
  868. for(i = 1; i < MAX_FONT_SIZES; ++i)
  869. f->req_sizes[i] = -1;
  870. scale = 1;
  871. voffset = 0;
  872. if(Cmd_Argc() >= 4)
  873. {
  874. for(sizes = 0, i = 3; i < Cmd_Argc(); ++i)
  875. {
  876. // special switches
  877. if (!strcmp(Cmd_Argv(i), "scale"))
  878. {
  879. i++;
  880. if (i < Cmd_Argc())
  881. scale = atof(Cmd_Argv(i));
  882. continue;
  883. }
  884. if (!strcmp(Cmd_Argv(i), "voffset"))
  885. {
  886. i++;
  887. if (i < Cmd_Argc())
  888. voffset = atof(Cmd_Argv(i));
  889. continue;
  890. }
  891. if (sizes == -1)
  892. continue; // no slot for other sizes
  893. // parse one of sizes
  894. sz = atof(Cmd_Argv(i));
  895. if (sz > 0.001f && sz < 1000.0f) // do not use crap sizes
  896. {
  897. // search for duplicated sizes
  898. int j;
  899. for (j=0; j<sizes; j++)
  900. if (f->req_sizes[j] == sz)
  901. break;
  902. if (j != sizes)
  903. continue; // sz already in req_sizes, don't add it again
  904. if (sizes == MAX_FONT_SIZES)
  905. {
  906. Con_Printf("Warning: specified more than %i different font sizes, exceding ones are ignored\n", MAX_FONT_SIZES);
  907. sizes = -1;
  908. continue;
  909. }
  910. f->req_sizes[sizes] = sz;
  911. sizes++;
  912. }
  913. }
  914. }
  915. LoadFont(true, mainfont, f, scale, voffset);
  916. }
  917. /*
  918. ===============
  919. Draw_Init
  920. ===============
  921. */
  922. static void gl_draw_start(void)
  923. {
  924. int i;
  925. char vabuf[1024];
  926. drawtexturepool = R_AllocTexturePool();
  927. numcachepics = 0;
  928. memset(cachepichash, 0, sizeof(cachepichash));
  929. font_start();
  930. // load default font textures
  931. for(i = 0; i < dp_fonts.maxsize; ++i)
  932. if (dp_fonts.f[i].title[0])
  933. LoadFont(false, va(vabuf, sizeof(vabuf), "gfx/font_%s", dp_fonts.f[i].title), &dp_fonts.f[i], 1, 0);
  934. // draw the loading screen so people have something to see in the newly opened window
  935. SCR_UpdateLoadingScreen(true, true);
  936. }
  937. static void gl_draw_shutdown(void)
  938. {
  939. font_shutdown();
  940. R_FreeTexturePool(&drawtexturepool);
  941. numcachepics = 0;
  942. memset(cachepichash, 0, sizeof(cachepichash));
  943. }
  944. static void gl_draw_newmap(void)
  945. {
  946. font_newmap();
  947. }
  948. void GL_Draw_Init (void)
  949. {
  950. int i, j;
  951. Cvar_RegisterVariable(&r_font_postprocess_blur);
  952. Cvar_RegisterVariable(&r_font_postprocess_outline);
  953. Cvar_RegisterVariable(&r_font_postprocess_shadow_x);
  954. Cvar_RegisterVariable(&r_font_postprocess_shadow_y);
  955. Cvar_RegisterVariable(&r_font_postprocess_shadow_z);
  956. Cvar_RegisterVariable(&r_font_hinting);
  957. Cvar_RegisterVariable(&r_font_antialias);
  958. Cvar_RegisterVariable(&r_textshadow);
  959. Cvar_RegisterVariable(&r_textbrightness);
  960. Cvar_RegisterVariable(&r_textcontrast);
  961. Cvar_RegisterVariable(&r_nearest_2d);
  962. Cvar_RegisterVariable(&r_nearest_conchars);
  963. // allocate fonts storage
  964. fonts_mempool = Mem_AllocPool("FONTS", 0, NULL);
  965. dp_fonts.maxsize = MAX_FONTS;
  966. dp_fonts.f = (dp_font_t *)Mem_Alloc(fonts_mempool, sizeof(dp_font_t) * dp_fonts.maxsize);
  967. memset(dp_fonts.f, 0, sizeof(dp_font_t) * dp_fonts.maxsize);
  968. // assign starting font names
  969. strlcpy(FONT_DEFAULT->title, "default", sizeof(FONT_DEFAULT->title));
  970. strlcpy(FONT_DEFAULT->texpath, "gfx/conchars", sizeof(FONT_DEFAULT->texpath));
  971. strlcpy(FONT_CONSOLE->title, "console", sizeof(FONT_CONSOLE->title));
  972. strlcpy(FONT_SBAR->title, "sbar", sizeof(FONT_SBAR->title));
  973. strlcpy(FONT_NOTIFY->title, "notify", sizeof(FONT_NOTIFY->title));
  974. strlcpy(FONT_CHAT->title, "chat", sizeof(FONT_CHAT->title));
  975. strlcpy(FONT_CENTERPRINT->title, "centerprint", sizeof(FONT_CENTERPRINT->title));
  976. strlcpy(FONT_INFOBAR->title, "infobar", sizeof(FONT_INFOBAR->title));
  977. strlcpy(FONT_MENU->title, "menu", sizeof(FONT_MENU->title));
  978. for(i = 0, j = 0; i < MAX_USERFONTS; ++i)
  979. if(!FONT_USER(i)->title[0])
  980. dpsnprintf(FONT_USER(i)->title, sizeof(FONT_USER(i)->title), "user%d", j++);
  981. Cmd_AddCommand ("loadfont",LoadFont_f, "loadfont function tganame loads a font; example: loadfont console gfx/veramono; loadfont without arguments lists the available functions");
  982. R_RegisterModule("GL_Draw", gl_draw_start, gl_draw_shutdown, gl_draw_newmap, NULL, NULL);
  983. }
  984. static void _DrawQ_Setup(void) // see R_ResetViewRendering2D
  985. {
  986. if (r_refdef.draw2dstage == 1)
  987. return;
  988. r_refdef.draw2dstage = 1;
  989. R_ResetViewRendering2D_Common(0, NULL, NULL, vid_conwidth.integer, vid_conheight.integer);
  990. }
  991. qboolean r_draw2d_force = false;
  992. static void _DrawQ_SetupAndProcessDrawFlag(int flags, cachepic_t *pic, float alpha)
  993. {
  994. _DrawQ_Setup();
  995. if(!r_draw2d.integer && !r_draw2d_force)
  996. return;
  997. DrawQ_ProcessDrawFlag(flags, (alpha < 1) || (pic && pic->hasalpha));
  998. }
  999. void DrawQ_ProcessDrawFlag(int flags, qboolean alpha)
  1000. {
  1001. if(flags == DRAWFLAG_ADDITIVE)
  1002. {
  1003. GL_DepthMask(false);
  1004. GL_BlendFunc(alpha ? GL_SRC_ALPHA : GL_ONE, GL_ONE);
  1005. }
  1006. else if(flags == DRAWFLAG_MODULATE)
  1007. {
  1008. GL_DepthMask(false);
  1009. GL_BlendFunc(GL_DST_COLOR, GL_ZERO);
  1010. }
  1011. else if(flags == DRAWFLAG_2XMODULATE)
  1012. {
  1013. GL_DepthMask(false);
  1014. GL_BlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
  1015. }
  1016. else if(flags == DRAWFLAG_SCREEN)
  1017. {
  1018. GL_DepthMask(false);
  1019. GL_BlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
  1020. }
  1021. else if(alpha)
  1022. {
  1023. GL_DepthMask(false);
  1024. GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  1025. }
  1026. else
  1027. {
  1028. GL_DepthMask(true);
  1029. GL_BlendFunc(GL_ONE, GL_ZERO);
  1030. }
  1031. }
  1032. void DrawQ_Pic(float x, float y, cachepic_t *pic, float width, float height, float red, float green, float blue, float alpha, int flags)
  1033. {
  1034. float floats[36];
  1035. _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
  1036. if(!r_draw2d.integer && !r_draw2d_force)
  1037. return;
  1038. // R_Mesh_ResetTextureState();
  1039. floats[12] = 0.0f;floats[13] = 0.0f;
  1040. floats[14] = 1.0f;floats[15] = 0.0f;
  1041. floats[16] = 1.0f;floats[17] = 1.0f;
  1042. floats[18] = 0.0f;floats[19] = 1.0f;
  1043. floats[20] = floats[24] = floats[28] = floats[32] = red;
  1044. floats[21] = floats[25] = floats[29] = floats[33] = green;
  1045. floats[22] = floats[26] = floats[30] = floats[34] = blue;
  1046. floats[23] = floats[27] = floats[31] = floats[35] = alpha;
  1047. if (pic)
  1048. {
  1049. if (width == 0)
  1050. width = pic->width;
  1051. if (height == 0)
  1052. height = pic->height;
  1053. R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
  1054. #if 0
  1055. // AK07: lets be texel correct on the corners
  1056. {
  1057. float horz_offset = 0.5f / pic->width;
  1058. float vert_offset = 0.5f / pic->height;
  1059. floats[12] = 0.0f + horz_offset;floats[13] = 0.0f + vert_offset;
  1060. floats[14] = 1.0f - horz_offset;floats[15] = 0.0f + vert_offset;
  1061. floats[16] = 1.0f - horz_offset;floats[17] = 1.0f - vert_offset;
  1062. floats[18] = 0.0f + horz_offset;floats[19] = 1.0f - vert_offset;
  1063. }
  1064. #endif
  1065. }
  1066. else
  1067. R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
  1068. floats[2] = floats[5] = floats[8] = floats[11] = 0;
  1069. floats[0] = floats[9] = x;
  1070. floats[1] = floats[4] = y;
  1071. floats[3] = floats[6] = x + width;
  1072. floats[7] = floats[10] = y + height;
  1073. R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
  1074. R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
  1075. }
  1076. 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)
  1077. {
  1078. float floats[36];
  1079. float af = DEG2RAD(-angle); // forward
  1080. float ar = DEG2RAD(-angle + 90); // right
  1081. float sinaf = sin(af);
  1082. float cosaf = cos(af);
  1083. float sinar = sin(ar);
  1084. float cosar = cos(ar);
  1085. _DrawQ_SetupAndProcessDrawFlag(flags, pic, alpha);
  1086. if(!r_draw2d.integer && !r_draw2d_force)
  1087. return;
  1088. // R_Mesh_ResetTextureState();
  1089. if (pic)
  1090. {
  1091. if (width == 0)
  1092. width = pic->width;
  1093. if (height == 0)
  1094. height = pic->height;
  1095. R_SetupShader_Generic(Draw_GetPicTexture(pic), NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
  1096. }
  1097. else
  1098. R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
  1099. floats[2] = floats[5] = floats[8] = floats[11] = 0;
  1100. // top left
  1101. floats[0] = x - cosaf*org_x - cosar*org_y;
  1102. floats[1] = y - sinaf*org_x - sinar*org_y;
  1103. // top right
  1104. floats[3] = x + cosaf*(width-org_x) - cosar*org_y;
  1105. floats[4] = y + sinaf*(width-org_x) - sinar*org_y;
  1106. // bottom right
  1107. floats[6] = x + cosaf*(width-org_x) + cosar*(height-org_y);
  1108. floats[7] = y + sinaf*(width-org_x) + sinar*(height-org_y);
  1109. // bottom left
  1110. floats[9] = x - cosaf*org_x + cosar*(height-org_y);
  1111. floats[10] = y - sinaf*org_x + sinar*(height-org_y);
  1112. floats[12] = 0.0f;floats[13] = 0.0f;
  1113. floats[14] = 1.0f;floats[15] = 0.0f;
  1114. floats[16] = 1.0f;floats[17] = 1.0f;
  1115. floats[18] = 0.0f;floats[19] = 1.0f;
  1116. floats[20] = floats[24] = floats[28] = floats[32] = red;
  1117. floats[21] = floats[25] = floats[29] = floats[33] = green;
  1118. floats[22] = floats[26] = floats[30] = floats[34] = blue;
  1119. floats[23] = floats[27] = floats[31] = floats[35] = alpha;
  1120. R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
  1121. R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
  1122. }
  1123. void DrawQ_Fill(float x, float y, float width, float height, float red, float green, float blue, float alpha, int flags)
  1124. {
  1125. float floats[36];
  1126. _DrawQ_SetupAndProcessDrawFlag(flags, NULL, alpha);
  1127. if(!r_draw2d.integer && !r_draw2d_force)
  1128. return;
  1129. // R_Mesh_ResetTextureState();
  1130. R_SetupShader_Generic_NoTexture((flags & DRAWFLAGS_BLEND) ? false : true, true);
  1131. floats[2] = floats[5] = floats[8] = floats[11] = 0;
  1132. floats[0] = floats[9] = x;
  1133. floats[1] = floats[4] = y;
  1134. floats[3] = floats[6] = x + width;
  1135. floats[7] = floats[10] = y + height;
  1136. floats[12] = 0.0f;floats[13] = 0.0f;
  1137. floats[14] = 1.0f;floats[15] = 0.0f;
  1138. floats[16] = 1.0f;floats[17] = 1.0f;
  1139. floats[18] = 0.0f;floats[19] = 1.0f;
  1140. floats[20] = floats[24] = floats[28] = floats[32] = red;
  1141. floats[21] = floats[25] = floats[29] = floats[33] = green;
  1142. floats[22] = floats[26] = floats[30] = floats[34] = blue;
  1143. floats[23] = floats[27] = floats[31] = floats[35] = alpha;
  1144. R_Mesh_PrepareVertices_Generic_Arrays(4, floats, floats + 20, floats + 12);
  1145. R_Mesh_Draw(0, 4, 0, 2, polygonelement3i, NULL, 0, polygonelement3s, NULL, 0);
  1146. }
  1147. /// color tag printing
  1148. static const vec4_t string_colors[] =
  1149. {
  1150. // Quake3 colors
  1151. // LordHavoc: why on earth is cyan before magenta in Quake3?
  1152. // LordHavoc: note: Doom3 uses white for [0] and [7]
  1153. {0.0, 0.0, 0.0, 1.0}, // black
  1154. {1.0, 0.0, 0.0, 1.0}, // red
  1155. {0.0, 1.0, 0.0, 1.0}, // green
  1156. {1.0, 1.0, 0.0, 1.0}, // yellow
  1157. {0.0, 0.0, 1.0, 1.0}, // blue
  1158. {0.0, 1.0, 1.0, 1.0}, // cyan
  1159. {1.0, 0.0, 1.0, 1.0}, // magenta
  1160. {1.0, 1.0, 1.0, 1.0}, // white
  1161. // [515]'s BX_COLOREDTEXT extension
  1162. {1.0, 1.0, 1.0, 0.5}, // half transparent
  1163. {0.5, 0.5, 0.5, 1.0} // half brightness
  1164. // Black's color table
  1165. //{1.0, 1.0, 1.0, 1.0},
  1166. //{1.0, 0.0, 0.0, 1.0},
  1167. //{0.0, 1.0, 0.0, 1.0},
  1168. //{0.0, 0.0, 1.0, 1.0},
  1169. //{1.0, 1.0, 0.0, 1.0},
  1170. //{0.0, 1.0, 1.0, 1.0},
  1171. //{1.0, 0.0, 1.0, 1.0},
  1172. //{0.1, 0.1, 0.1, 1.0}
  1173. };
  1174. #define STRING_COLORS_COUNT (sizeof(string_colors) / sizeof(vec4_t))
  1175. static void DrawQ_GetTextColor(float color[4], int colorindex, float r, float g, float b, float a, qboolean shadow)
  1176. {
  1177. float C = r_textcontrast.value;
  1178. float B = r_textbrightness.value;
  1179. if (colorindex & 0x10000) // that bit means RGB color
  1180. {
  1181. color[0] = ((colorindex >> 12) & 0xf) / 15.0;
  1182. color[1] = ((colorindex >> 8) & 0xf) / 15.0;
  1183. color[2] = ((colorindex >> 4) & 0xf) / 15.0;
  1184. color[3] = (colorindex & 0xf) / 15.0;
  1185. }
  1186. else
  1187. Vector4Copy(string_colors[colorindex], color);
  1188. Vector4Set(color, color[0] * r * C + B, color[1] * g * C + B, color[2] * b * C + B, color[3] * a);
  1189. if (shadow)
  1190. {
  1191. float shadowalpha = (color[0]+color[1]+color[2]) * 0.8;
  1192. Vector4Set(color, 0, 0, 0, color[3] * bound(0, shadowalpha, 1));
  1193. }
  1194. }
  1195. // NOTE: this function always draws exactly one character if maxwidth <= 0
  1196. 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)
  1197. {
  1198. const char *text_start = text;
  1199. int colorindex = STRING_COLOR_DEFAULT;
  1200. size_t i;
  1201. float x = 0;
  1202. Uchar ch, mapch, nextch;
  1203. Uchar prevch = 0; // used for kerning
  1204. int tempcolorindex;
  1205. float kx;
  1206. int map_index = 0;
  1207. size_t bytes_left;
  1208. ft2_font_map_t *fontmap = NULL;
  1209. ft2_font_map_t *map = NULL;
  1210. //ft2_font_map_t *prevmap = NULL;
  1211. ft2_font_t *ft2 = fnt->ft2;
  1212. // float ftbase_x;
  1213. qboolean snap = true;
  1214. qboolean least_one = false;
  1215. float dw; // display w
  1216. //float dh; // display h
  1217. const float *width_of;
  1218. if (!h) h = w;
  1219. if (!h) {
  1220. w = h = 1;
  1221. snap = false;
  1222. }
  1223. // do this in the end
  1224. w *= fnt->settings.scale;
  1225. h *= fnt->settings.scale;
  1226. // find the most fitting size:
  1227. if (ft2 != NULL)
  1228. {
  1229. if (snap)
  1230. map_index = Font_IndexForSize(ft2, h, &w, &h);
  1231. else
  1232. map_index = Font_IndexForSize(ft2, h, NULL, NULL);
  1233. fontmap = Font_MapForIndex(ft2, map_index);
  1234. }
  1235. dw = w * sw;
  1236. //dh = h * sh;
  1237. if (*maxlen < 1)
  1238. *maxlen = 1<<30;
  1239. if (!outcolor || *outcolor == -1)
  1240. colorindex = STRING_COLOR_DEFAULT;
  1241. else
  1242. colorindex = *outcolor;
  1243. // maxwidth /= fnt->scale; // w and h are multiplied by it already
  1244. // ftbase_x = snap_to_pixel_x(0);
  1245. if(maxwidth <= 0)
  1246. {
  1247. least_one = true;
  1248. maxwidth = -maxwidth;
  1249. }
  1250. //if (snap)
  1251. // x = snap_to_pixel_x(x, 0.4); // haha, it's 0 anyway
  1252. if (fontmap)
  1253. width_of = fontmap->width_of;
  1254. else
  1255. width_of = fnt->width_of;
  1256. i = 0;
  1257. while (((bytes_left = *maxlen - (text - text_start)) > 0) && *text)
  1258. {
  1259. size_t i0 = i;
  1260. nextch = ch = u8_getnchar(text, &text, bytes_left);
  1261. i = text - text_start;
  1262. if (!ch)
  1263. break;
  1264. if (ch == ' ' && !fontmap)
  1265. {
  1266. if(!least_one || i0) // never skip the first character
  1267. if(x + width_of[(int) ' '] * dw > maxwidth)
  1268. {
  1269. i = i0;
  1270. break; // oops, can't draw this
  1271. }
  1272. x += width_of[(int) ' '] * dw;
  1273. continue;
  1274. }
  1275. // i points to the char after ^
  1276. if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < *maxlen)
  1277. {
  1278. ch = *text; // colors are ascii, so no u8_ needed
  1279. if (ch <= '9' && ch >= '0') // ^[0-9] found
  1280. {
  1281. colorindex = ch - '0';
  1282. ++text;
  1283. ++i;
  1284. continue;
  1285. }
  1286. // i points to the char after ^...
  1287. // i+3 points to 3 in ^x123
  1288. // i+3 == *maxlen would mean that char is missing
  1289. else if (ch == STRING_COLOR_RGB_TAG_CHAR && i + 3 < *maxlen ) // ^x found
  1290. {
  1291. // building colorindex...
  1292. ch = tolower(text[1]);
  1293. tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
  1294. if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
  1295. else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
  1296. else tempcolorindex = 0;
  1297. if (tempcolorindex)
  1298. {
  1299. ch = tolower(text[2]);
  1300. if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
  1301. else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
  1302. else tempcolorindex = 0;
  1303. if (tempcolorindex)
  1304. {
  1305. ch = tolower(text[3]);
  1306. if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
  1307. else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
  1308. else tempcolorindex = 0;
  1309. if (tempcolorindex)
  1310. {
  1311. colorindex = tempcolorindex | 0xf;
  1312. // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
  1313. i+=4;
  1314. text += 4;
  1315. continue;
  1316. }
  1317. }
  1318. }
  1319. }
  1320. else if (ch == STRING_COLOR_TAG) // ^^ found, ignore the first ^ and go to print the second
  1321. {
  1322. i++;
  1323. text++;
  1324. }
  1325. i--;
  1326. }
  1327. ch = nextch;
  1328. if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
  1329. {
  1330. if (ch > 0xE000)
  1331. ch -= 0xE000;
  1332. if (ch > 0xFF)
  1333. continue;
  1334. if (fontmap)
  1335. map = ft2_oldstyle_map;
  1336. prevch = 0;
  1337. if(!least_one || i0) // never skip the first character
  1338. if(x + width_of[ch] * dw > maxwidth)
  1339. {
  1340. i = i0;
  1341. break; // oops, can't draw this
  1342. }
  1343. x += width_of[ch] * dw;
  1344. } else {
  1345. if (!map || map == ft2_oldstyle_map || ch < map->start || ch >= map->start + FONT_CHARS_PER_MAP)
  1346. {
  1347. map = FontMap_FindForChar(fontmap, ch);
  1348. if (!map)
  1349. {
  1350. if (!Font_LoadMapForIndex(ft2, map_index, ch, &map))
  1351. break;
  1352. if (!map)
  1353. break;
  1354. }
  1355. }
  1356. mapch = ch - map->start;
  1357. if (prevch && Font_GetKerningForMap(ft2, map_index, w, h, prevch, ch, &kx, NULL))
  1358. x += kx * dw;
  1359. x += map->glyphs[mapch].advance_x * dw;
  1360. //prevmap = map;
  1361. prevch = ch;
  1362. }
  1363. }
  1364. *maxlen = i;
  1365. if (outcolor)
  1366. *outcolor = colorindex;
  1367. return x;
  1368. }
  1369. float DrawQ_Color[4];
  1370. 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)
  1371. {
  1372. int shadow, colorindex = STRING_COLOR_DEFAULT;
  1373. size_t i;
  1374. float x = startx, y, s, t, u, v, thisw;
  1375. float *av, *at, *ac;
  1376. int batchcount;
  1377. static float vertex3f[QUADELEMENTS_MAXQUADS*4*3];
  1378. static float texcoord2f[QUADELEMENTS_MAXQUADS*4*2];
  1379. static float color4f[QUADELEMENTS_MAXQUADS*4*4];
  1380. Uchar ch, mapch, nextch;
  1381. Uchar prevch = 0; // used for kerning
  1382. int tempcolorindex;
  1383. int map_index = 0;
  1384. //ft2_font_map_t *prevmap = NULL; // the previous map
  1385. ft2_font_map_t *map = NULL; // the currently used map
  1386. ft2_font_map_t *fontmap = NULL; // the font map for the size
  1387. float ftbase_y;
  1388. const char *text_start = text;
  1389. float kx, ky;
  1390. ft2_font_t *ft2 = fnt->ft2;
  1391. qboolean snap = true;
  1392. float pix_x, pix_y;
  1393. size_t bytes_left;
  1394. float dw, dh;
  1395. const float *width_of;
  1396. int tw, th;
  1397. tw = R_TextureWidth(fnt->tex);
  1398. th = R_TextureHeight(fnt->tex);
  1399. if (!h) h = w;
  1400. if (!h) {
  1401. h = w = 1;
  1402. snap = false;
  1403. }
  1404. starty -= (fnt->settings.scale - 1) * h * 0.5 - fnt->settings.voffset*h; // center & offset
  1405. w *= fnt->settings.scale;
  1406. h *= fnt->settings.scale;
  1407. if (ft2 != NULL)
  1408. {
  1409. if (snap)
  1410. map_index = Font_IndexForSize(ft2, h, &w, &h);
  1411. else
  1412. map_index = Font_IndexForSize(ft2, h, NULL, NULL);
  1413. fontmap = Font_MapForIndex(ft2, map_index);
  1414. }
  1415. dw = w * sw;
  1416. dh = h * sh;
  1417. // draw the font at its baseline when using freetype
  1418. //ftbase_x = 0;
  1419. ftbase_y = dh * (4.5/6.0);
  1420. if (maxlen < 1)
  1421. maxlen = 1<<30;
  1422. _DrawQ_SetupAndProcessDrawFlag(flags, NULL, 0);
  1423. if(!r_draw2d.integer && !r_draw2d_force)
  1424. return startx + DrawQ_TextWidth_UntilWidth_TrackColors_Scale(text, &maxlen, w, h, sw, sh, NULL, ignorecolorcodes, fnt, 1000000000);
  1425. // R_Mesh_ResetTextureState();
  1426. if (!fontmap)
  1427. R_Mesh_TexBind(0, fnt->tex);
  1428. R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
  1429. ac = color4f;
  1430. at = texcoord2f;
  1431. av = vertex3f;
  1432. batchcount = 0;
  1433. //ftbase_x = snap_to_pixel_x(ftbase_x);
  1434. if(snap)
  1435. {
  1436. startx = snap_to_pixel_x(startx, 0.4);
  1437. starty = snap_to_pixel_y(starty, 0.4);
  1438. ftbase_y = snap_to_pixel_y(ftbase_y, 0.3);
  1439. }
  1440. pix_x = vid.width / vid_conwidth.value;
  1441. pix_y = vid.height / vid_conheight.value;
  1442. if (fontmap)
  1443. width_of = fontmap->width_of;
  1444. else
  1445. width_of = fnt->width_of;
  1446. for (shadow = r_textshadow.value != 0 && basealpha > 0;shadow >= 0;shadow--)
  1447. {
  1448. prevch = 0;
  1449. text = text_start;
  1450. if (!outcolor || *outcolor == -1)
  1451. colorindex = STRING_COLOR_DEFAULT;
  1452. else
  1453. colorindex = *outcolor;
  1454. DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
  1455. x = startx;
  1456. y = starty;
  1457. /*
  1458. if (shadow)
  1459. {
  1460. x += r_textshadow.value * vid.width / vid_conwidth.value;
  1461. y += r_textshadow.value * vid.height / vid_conheight.value;
  1462. }
  1463. */
  1464. while (((bytes_left = maxlen - (text - text_start)) > 0) && *text)
  1465. {
  1466. nextch = ch = u8_getnchar(text, &text, bytes_left);
  1467. i = text - text_start;
  1468. if (!ch)
  1469. break;
  1470. if (ch == ' ' && !fontmap)
  1471. {
  1472. x += width_of[(int) ' '] * dw;
  1473. continue;
  1474. }
  1475. if (ch == STRING_COLOR_TAG && !ignorecolorcodes && i < maxlen)
  1476. {
  1477. ch = *text; // colors are ascii, so no u8_ needed
  1478. if (ch <= '9' && ch >= '0') // ^[0-9] found
  1479. {
  1480. colorindex = ch - '0';
  1481. DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
  1482. ++text;
  1483. ++i;
  1484. continue;
  1485. }
  1486. else if (ch == STRING_COLOR_RGB_TAG_CHAR && i+3 < maxlen ) // ^x found
  1487. {
  1488. // building colorindex...
  1489. ch = tolower(text[1]);
  1490. tempcolorindex = 0x10000; // binary: 1,0000,0000,0000,0000
  1491. if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 12;
  1492. else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 12;
  1493. else tempcolorindex = 0;
  1494. if (tempcolorindex)
  1495. {
  1496. ch = tolower(text[2]);
  1497. if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 8;
  1498. else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 8;
  1499. else tempcolorindex = 0;
  1500. if (tempcolorindex)
  1501. {
  1502. ch = tolower(text[3]);
  1503. if (ch <= '9' && ch >= '0') tempcolorindex |= (ch - '0') << 4;
  1504. else if (ch >= 'a' && ch <= 'f') tempcolorindex |= (ch - 87) << 4;
  1505. else tempcolorindex = 0;
  1506. if (tempcolorindex)
  1507. {
  1508. colorindex = tempcolorindex | 0xf;
  1509. // ...done! now colorindex has rgba codes (1,rrrr,gggg,bbbb,aaaa)
  1510. //Con_Printf("^1colorindex:^7 %x\n", colorindex);
  1511. DrawQ_GetTextColor(DrawQ_Color, colorindex, basered, basegreen, baseblue, basealpha, shadow != 0);
  1512. i+=4;
  1513. text+=4;
  1514. continue;
  1515. }
  1516. }
  1517. }
  1518. }
  1519. else if (ch == STRING_COLOR_TAG)
  1520. {
  1521. i++;
  1522. text++;
  1523. }
  1524. i--;
  1525. }
  1526. // get the backup
  1527. ch = nextch;
  1528. // using a value of -1 for the oldstyle map because NULL means uninitialized...
  1529. // this way we don't need to rebind fnt->tex for every old-style character
  1530. // E000..E0FF: emulate old-font characters (to still have smileys and such available)
  1531. if (shadow)
  1532. {
  1533. x += 1.0/pix_x * r_textshadow.value;
  1534. y += 1.0/pix_y * r_textshadow.value;
  1535. }
  1536. if (!fontmap || (ch <= 0xFF && fontmap->glyphs[ch].image) || (ch >= 0xE000 && ch <= 0xE0FF))
  1537. {
  1538. if (ch >= 0xE000)
  1539. ch -= 0xE000;
  1540. if (ch > 0xFF)
  1541. goto out;
  1542. if (fontmap)
  1543. {
  1544. if (map != ft2_oldstyle_map)
  1545. {
  1546. if (batchcount)
  1547. {
  1548. // switching from freetype to non-freetype rendering
  1549. R_Mesh_PrepareVertices_Generic_Arrays(batchcount * 4, vertex3f, color4f, texcoord2f);
  1550. R_Mesh_Draw(0, batchcount * 4, 0, batchcount * 2, quadelement3i, NULL, 0, quadelement3s, NULL, 0);
  1551. batchcount = 0;
  1552. ac = color4f;
  1553. at = texcoord2f;
  1554. av = vertex3f;
  1555. }
  1556. R_SetupShader_Generic(fnt->tex, NULL, GL_MODULATE, 1, (flags & DRAWFLAGS_BLEND) ? false : true, true, false);
  1557. map = ft2_oldstyle_map;
  1558. }
  1559. }
  1560. prevch = 0;
  1561. //num = (unsigned char) text[i];
  1562. //thisw = fnt->width_of[num];
  1563. thisw = fnt->width_of[ch];
  1564. // FIXME make these smaller to just include the occupied part of the character for slightly faster rendering
  1565. if (r_nearest_conchars.integer)
  1566. {
  1567. s = (ch & 15)*0.0625f;
  1568. t = (ch >> 4)*0.0625f;
  1569. u = 0.0625f * thisw;
  1570. v = 0.0625f;
  1571. }
  1572. else
  1573. {
  1574. s = (ch & 15)

Large files files are truncated, but you can click here to view the full file