1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1993-1996 by id Software, Inc.
4 // Copyright (C) 1998-2000 by DooM Legacy Team.
5 // Copyright (C) 1999-2020 by Sonic Team Junior.
6 //
7 // This program is free software distributed under the
8 // terms of the GNU General Public License, version 2.
9 // See the 'LICENSE' file for more details.
10 //-----------------------------------------------------------------------------
11 /// \file  v_video.c
12 /// \brief Gamma correction LUT stuff
13 ///        Functions to draw patches (by post) directly to screen.
14 ///        Functions to blit a block to the screen.
15 
16 #include "doomdef.h"
17 #include "r_local.h"
18 #include "p_local.h" // stplyr
19 #include "g_game.h" // players
20 #include "v_video.h"
21 #include "st_stuff.h"
22 #include "hu_stuff.h"
23 #include "f_finale.h"
24 #include "r_draw.h"
25 #include "console.h"
26 
27 #include "i_video.h" // rendermode
28 #include "z_zone.h"
29 #include "m_misc.h"
30 #include "m_random.h"
31 #include "doomstat.h"
32 
33 #ifdef HWRENDER
34 #include "hardware/hw_glob.h"
35 #endif
36 
37 // Each screen is [vid.width*vid.height];
38 UINT8 *screens[5];
39 // screens[0] = main display window
40 // screens[1] = back screen, alternative blitting
41 // screens[2] = screenshot buffer, gif movie buffer
42 // screens[3] = fade screen start
43 // screens[4] = fade screen end, postimage tempoarary buffer
44 
45 static CV_PossibleValue_t ticrate_cons_t[] = {{0, "No"}, {1, "Full"}, {2, "Compact"}, {0, NULL}};
46 consvar_t cv_ticrate = CVAR_INIT ("showfps", "No", CV_SAVE, ticrate_cons_t, NULL);
47 
48 static void CV_palette_OnChange(void);
49 
50 static CV_PossibleValue_t gamma_cons_t[] = {{-15, "MIN"}, {5, "MAX"}, {0, NULL}};
51 consvar_t cv_globalgamma = CVAR_INIT ("gamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange);
52 
53 static CV_PossibleValue_t saturation_cons_t[] = {{0, "MIN"}, {10, "MAX"}, {0, NULL}};
54 consvar_t cv_globalsaturation = CVAR_INIT ("saturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange);
55 
56 #define huecoloursteps 4
57 
58 static CV_PossibleValue_t hue_cons_t[] = {{0, "MIN"}, {(huecoloursteps*6)-1, "MAX"}, {0, NULL}};
59 consvar_t cv_rhue = CVAR_INIT ("rhue",  "0", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange);
60 consvar_t cv_yhue = CVAR_INIT ("yhue",  "4", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange);
61 consvar_t cv_ghue = CVAR_INIT ("ghue",  "8", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange);
62 consvar_t cv_chue = CVAR_INIT ("chue", "12", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange);
63 consvar_t cv_bhue = CVAR_INIT ("bhue", "16", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange);
64 consvar_t cv_mhue = CVAR_INIT ("mhue", "20", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange);
65 
66 consvar_t cv_rgamma = CVAR_INIT ("rgamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange);
67 consvar_t cv_ygamma = CVAR_INIT ("ygamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange);
68 consvar_t cv_ggamma = CVAR_INIT ("ggamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange);
69 consvar_t cv_cgamma = CVAR_INIT ("cgamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange);
70 consvar_t cv_bgamma = CVAR_INIT ("bgamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange);
71 consvar_t cv_mgamma = CVAR_INIT ("mgamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange);
72 
73 consvar_t cv_rsaturation = CVAR_INIT ("rsaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange);
74 consvar_t cv_ysaturation = CVAR_INIT ("ysaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange);
75 consvar_t cv_gsaturation = CVAR_INIT ("gsaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange);
76 consvar_t cv_csaturation = CVAR_INIT ("csaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange);
77 consvar_t cv_bsaturation = CVAR_INIT ("bsaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange);
78 consvar_t cv_msaturation = CVAR_INIT ("msaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange);
79 
80 static CV_PossibleValue_t constextsize_cons_t[] = {
81 	{V_NOSCALEPATCH, "Small"}, {V_SMALLSCALEPATCH, "Medium"}, {V_MEDSCALEPATCH, "Large"}, {0, "Huge"},
82 	{0, NULL}};
83 static void CV_constextsize_OnChange(void);
84 consvar_t cv_constextsize = CVAR_INIT ("con_textsize", "Medium", CV_SAVE|CV_CALL, constextsize_cons_t, CV_constextsize_OnChange);
85 
86 // local copy of the palette for V_GetColor()
87 RGBA_t *pLocalPalette = NULL;
88 RGBA_t *pMasterPalette = NULL;
89 
90 /*
91 The following was an extremely helpful resource when developing my Colour Cube LUT.
92 http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter24.html
93 Please check it out if you're trying to maintain this.
94 toast 18/04/17
95 */
96 float Cubepal[2][2][2][3];
97 boolean Cubeapply = false;
98 
99 // returns whether to apply cube, selectively avoiding expensive operations
InitCube(void)100 static boolean InitCube(void)
101 {
102 	boolean apply = false;
103 	UINT8 q;
104 	float working[2][2][2][3] = // the initial positions of the corners of the colour cube!
105 	{
106 		{
107 			{
108 				{0.0, 0.0, 0.0}, // black corner
109 				{0.0, 0.0, 1.0}  // blue corner
110 			},
111 			{
112 				{0.0, 1.0, 0.0}, // green corner
113 				{0.0, 1.0, 1.0}  // cyan corner
114 			}
115 		},
116 		{
117 			{
118 				{1.0, 0.0, 0.0}, // red corner
119 				{1.0, 0.0, 1.0}  // magenta corner
120 			},
121 			{
122 				{1.0, 1.0, 0.0}, // yellow corner
123 				{1.0, 1.0, 1.0}  // white corner
124 			}
125 		}
126 	};
127 	float desatur[3]; // grey
128 	float globalgammamul, globalgammaoffs;
129 	boolean doinggamma;
130 
131 #define diffcons(cv) (cv.value != atoi(cv.defaultvalue))
132 
133 	doinggamma = diffcons(cv_globalgamma);
134 
135 #define gammascale 8
136 	globalgammamul = (cv_globalgamma.value ? ((255 - (gammascale*abs(cv_globalgamma.value)))/255.0) : 1.0);
137 	globalgammaoffs = ((cv_globalgamma.value > 0) ? ((gammascale*cv_globalgamma.value)/255.0) : 0.0);
138 	desatur[0] = desatur[1] = desatur[2] = globalgammaoffs + (0.33*globalgammamul);
139 
140 	if (doinggamma
141 		|| diffcons(cv_rhue)
142 		|| diffcons(cv_yhue)
143 		|| diffcons(cv_ghue)
144 		|| diffcons(cv_chue)
145 		|| diffcons(cv_bhue)
146 		|| diffcons(cv_mhue)
147 		|| diffcons(cv_rgamma)
148 		|| diffcons(cv_ygamma)
149 		|| diffcons(cv_ggamma)
150 		|| diffcons(cv_cgamma)
151 		|| diffcons(cv_bgamma)
152 		|| diffcons(cv_mgamma)) // set the gamma'd/hued positions (saturation is done later)
153 	{
154 		float mod, tempgammamul, tempgammaoffs;
155 
156 		apply = true;
157 
158 		working[0][0][0][0] = working[0][0][0][1] = working[0][0][0][2] = globalgammaoffs;
159 		working[1][1][1][0] = working[1][1][1][1] = working[1][1][1][2] = globalgammaoffs+globalgammamul;
160 
161 #define dohue(hue, gamma, loc) \
162 		tempgammamul = (gamma ? ((255 - (gammascale*abs(gamma)))/255.0)*globalgammamul : globalgammamul);\
163 		tempgammaoffs = ((gamma > 0) ? ((gammascale*gamma)/255.0) + globalgammaoffs : globalgammaoffs);\
164 		mod = ((hue % huecoloursteps)*(tempgammamul)/huecoloursteps);\
165 		switch (hue/huecoloursteps)\
166 		{\
167 			case 0:\
168 			default:\
169 				loc[0] = tempgammaoffs+tempgammamul;\
170 				loc[1] = tempgammaoffs+mod;\
171 				loc[2] = tempgammaoffs;\
172 				break;\
173 			case 1:\
174 				loc[0] = tempgammaoffs+tempgammamul-mod;\
175 				loc[1] = tempgammaoffs+tempgammamul;\
176 				loc[2] = tempgammaoffs;\
177 				break;\
178 			case 2:\
179 				loc[0] = tempgammaoffs;\
180 				loc[1] = tempgammaoffs+tempgammamul;\
181 				loc[2] = tempgammaoffs+mod;\
182 				break;\
183 			case 3:\
184 				loc[0] = tempgammaoffs;\
185 				loc[1] = tempgammaoffs+tempgammamul-mod;\
186 				loc[2] = tempgammaoffs+tempgammamul;\
187 				break;\
188 			case 4:\
189 				loc[0] = tempgammaoffs+mod;\
190 				loc[1] = tempgammaoffs;\
191 				loc[2] = tempgammaoffs+tempgammamul;\
192 				break;\
193 			case 5:\
194 				loc[0] = tempgammaoffs+tempgammamul;\
195 				loc[1] = tempgammaoffs;\
196 				loc[2] = tempgammaoffs+tempgammamul-mod;\
197 				break;\
198 		}
199 		dohue(cv_rhue.value, cv_rgamma.value, working[1][0][0]);
200 		dohue(cv_yhue.value, cv_ygamma.value, working[1][1][0]);
201 		dohue(cv_ghue.value, cv_ggamma.value, working[0][1][0]);
202 		dohue(cv_chue.value, cv_cgamma.value, working[0][1][1]);
203 		dohue(cv_bhue.value, cv_bgamma.value, working[0][0][1]);
204 		dohue(cv_mhue.value, cv_mgamma.value, working[1][0][1]);
205 #undef dohue
206 	}
207 
208 #define dosaturation(a, e) a = ((1 - work)*e + work*a)
209 #define docvsat(cv_sat, hue, gamma, r, g, b) \
210 	if diffcons(cv_sat)\
211 	{\
212 		float work, mod, tempgammamul, tempgammaoffs;\
213 		apply = true;\
214 		work = (cv_sat.value/10.0);\
215 		mod = ((hue % huecoloursteps)*(1.0)/huecoloursteps);\
216 		if (hue & huecoloursteps)\
217 			mod = 2-mod;\
218 		else\
219 			mod += 1;\
220 		tempgammamul = (gamma ? ((255 - (gammascale*abs(gamma)))/255.0)*globalgammamul : globalgammamul);\
221 		tempgammaoffs = ((gamma > 0) ? ((gammascale*gamma)/255.0) + globalgammaoffs : globalgammaoffs);\
222 		for (q = 0; q < 3; q++)\
223 			dosaturation(working[r][g][b][q], (tempgammaoffs+(desatur[q]*mod*tempgammamul)));\
224 	}
225 
226 	docvsat(cv_rsaturation, cv_rhue.value, cv_rgamma.value, 1, 0, 0);
227 	docvsat(cv_ysaturation, cv_yhue.value, cv_ygamma.value, 1, 1, 0);
228 	docvsat(cv_gsaturation, cv_ghue.value, cv_ggamma.value, 0, 1, 0);
229 	docvsat(cv_csaturation, cv_chue.value, cv_cgamma.value, 0, 1, 1);
230 	docvsat(cv_bsaturation, cv_bhue.value, cv_bgamma.value, 0, 0, 1);
231 	docvsat(cv_msaturation, cv_mhue.value, cv_mgamma.value, 1, 0, 1);
232 
233 #undef gammascale
234 
235 	if diffcons(cv_globalsaturation)
236 	{
237 		float work = (cv_globalsaturation.value/10.0);
238 
239 		apply = true;
240 
241 		for (q = 0; q < 3; q++)
242 		{
243 			dosaturation(working[1][0][0][q], desatur[q]);
244 			dosaturation(working[0][1][0][q], desatur[q]);
245 			dosaturation(working[0][0][1][q], desatur[q]);
246 
247 			dosaturation(working[1][1][0][q], 2*desatur[q]);
248 			dosaturation(working[0][1][1][q], 2*desatur[q]);
249 			dosaturation(working[1][0][1][q], 2*desatur[q]);
250 		}
251 	}
252 
253 #undef dosaturation
254 
255 #undef diffcons
256 
257 	if (!apply)
258 		return false;
259 
260 #define dowork(i, j, k, l) \
261 	if (working[i][j][k][l] > 1.0)\
262 		working[i][j][k][l] = 1.0;\
263 	else if (working[i][j][k][l] < 0.0)\
264 		working[i][j][k][l] = 0.0;\
265 	Cubepal[i][j][k][l] = working[i][j][k][l]
266 	for (q = 0; q < 3; q++)
267 	{
268 		dowork(0, 0, 0, q);
269 		dowork(1, 0, 0, q);
270 		dowork(0, 1, 0, q);
271 		dowork(1, 1, 0, q);
272 		dowork(0, 0, 1, q);
273 		dowork(1, 0, 1, q);
274 		dowork(0, 1, 1, q);
275 		dowork(1, 1, 1, q);
276 	}
277 #undef dowork
278 
279 	return true;
280 }
281 
282 #ifdef BACKWARDSCOMPATCORRECTION
283 /*
284 So it turns out that the way gamma was implemented previously, the default
285 colour profile of the game was messed up. Since this bad decision has been
286 around for a long time, and the intent is to keep the base game looking the
287 same, I'm not gonna be the one to remove this base modification.
288 toast 20/04/17
289 ... welp yes i am (27/07/19, see the ifdef around it)
290 */
291 const UINT8 correctiontable[256] =
292 	{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
293 	17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,
294 	33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,
295 	49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,
296 	65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,
297 	81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,
298 	97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,
299 	113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,
300 	128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
301 	144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
302 	160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
303 	176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
304 	192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
305 	208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
306 	224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
307 	240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255};
308 #endif
309 
310 // keep a copy of the palette so that we can get the RGB value for a color index at any time.
LoadPalette(const char * lumpname)311 static void LoadPalette(const char *lumpname)
312 {
313 	lumpnum_t lumpnum = W_GetNumForName(lumpname);
314 	size_t i, palsize = W_LumpLength(lumpnum)/3;
315 	UINT8 *pal;
316 
317 	Cubeapply = InitCube();
318 
319 	Z_Free(pLocalPalette);
320 	Z_Free(pMasterPalette);
321 
322 	pLocalPalette = Z_Malloc(sizeof (*pLocalPalette)*palsize, PU_STATIC, NULL);
323 	pMasterPalette = Z_Malloc(sizeof (*pMasterPalette)*palsize, PU_STATIC, NULL);
324 
325 	pal = W_CacheLumpNum(lumpnum, PU_CACHE);
326 	for (i = 0; i < palsize; i++)
327 	{
328 #ifdef BACKWARDSCOMPATCORRECTION
329 		pMasterPalette[i].s.red = pLocalPalette[i].s.red = correctiontable[*pal++];
330 		pMasterPalette[i].s.green = pLocalPalette[i].s.green = correctiontable[*pal++];
331 		pMasterPalette[i].s.blue = pLocalPalette[i].s.blue = correctiontable[*pal++];
332 #else
333 		pMasterPalette[i].s.red = pLocalPalette[i].s.red = *pal++;
334 		pMasterPalette[i].s.green = pLocalPalette[i].s.green = *pal++;
335 		pMasterPalette[i].s.blue = pLocalPalette[i].s.blue = *pal++;
336 #endif
337 		pMasterPalette[i].s.alpha = pLocalPalette[i].s.alpha = 0xFF;
338 
339 		// lerp of colour cubing! if you want, make it smoother yourself
340 		if (Cubeapply)
341 			V_CubeApply(&pLocalPalette[i].s.red, &pLocalPalette[i].s.green, &pLocalPalette[i].s.blue);
342 	}
343 }
344 
V_CubeApply(UINT8 * red,UINT8 * green,UINT8 * blue)345 void V_CubeApply(UINT8 *red, UINT8 *green, UINT8 *blue)
346 {
347 	float working[4][3];
348 	float linear;
349 	UINT8 q;
350 
351 	if (!Cubeapply)
352 		return;
353 
354 	linear = (*red/255.0);
355 #define dolerp(e1, e2) ((1 - linear)*e1 + linear*e2)
356 	for (q = 0; q < 3; q++)
357 	{
358 		working[0][q] = dolerp(Cubepal[0][0][0][q], Cubepal[1][0][0][q]);
359 		working[1][q] = dolerp(Cubepal[0][1][0][q], Cubepal[1][1][0][q]);
360 		working[2][q] = dolerp(Cubepal[0][0][1][q], Cubepal[1][0][1][q]);
361 		working[3][q] = dolerp(Cubepal[0][1][1][q], Cubepal[1][1][1][q]);
362 	}
363 	linear = (*green/255.0);
364 	for (q = 0; q < 3; q++)
365 	{
366 		working[0][q] = dolerp(working[0][q], working[1][q]);
367 		working[1][q] = dolerp(working[2][q], working[3][q]);
368 	}
369 	linear = (*blue/255.0);
370 	for (q = 0; q < 3; q++)
371 	{
372 		working[0][q] = 255*dolerp(working[0][q], working[1][q]);
373 		if (working[0][q] > 255.0)
374 			working[0][q] = 255.0;
375 		else if (working[0][q]  < 0.0)
376 			working[0][q] = 0.0;
377 	}
378 #undef dolerp
379 
380 	*red = (UINT8)(working[0][0]);
381 	*green = (UINT8)(working[0][1]);
382 	*blue = (UINT8)(working[0][2]);
383 }
384 
R_GetPalname(UINT16 num)385 const char *R_GetPalname(UINT16 num)
386 {
387 	static char palname[9];
388 	char newpal[9] = "PLAYPAL";
389 
390 	if (num > 0 && num <= 10000)
391 		snprintf(newpal, 8, "PAL%04u", num-1);
392 
393 	strncpy(palname, newpal, 8);
394 	return palname;
395 }
396 
GetPalette(void)397 const char *GetPalette(void)
398 {
399 	if (gamestate == GS_LEVEL)
400 		return R_GetPalname(mapheaderinfo[gamemap-1]->palette);
401 	return "PLAYPAL";
402 }
403 
LoadMapPalette(void)404 static void LoadMapPalette(void)
405 {
406 	LoadPalette(GetPalette());
407 }
408 
409 // -------------+
410 // V_SetPalette : Set the current palette to use for palettized graphics
411 //              :
412 // -------------+
V_SetPalette(INT32 palettenum)413 void V_SetPalette(INT32 palettenum)
414 {
415 	if (!pLocalPalette)
416 		LoadMapPalette();
417 
418 #ifdef HWRENDER
419 	if (rendermode == render_opengl)
420 		HWR_SetPalette(&pLocalPalette[palettenum*256]);
421 #if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL)
422 	else
423 #endif
424 #endif
425 	if (rendermode != render_none)
426 		I_SetPalette(&pLocalPalette[palettenum*256]);
427 }
428 
V_SetPaletteLump(const char * pal)429 void V_SetPaletteLump(const char *pal)
430 {
431 	LoadPalette(pal);
432 #ifdef HWRENDER
433 	if (rendermode == render_opengl)
434 		HWR_SetPalette(pLocalPalette);
435 #if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL)
436 	else
437 #endif
438 #endif
439 	if (rendermode != render_none)
440 		I_SetPalette(pLocalPalette);
441 }
442 
CV_palette_OnChange(void)443 static void CV_palette_OnChange(void)
444 {
445 	// reload palette
446 	LoadMapPalette();
447 	V_SetPalette(0);
448 }
449 
450 #if defined (__GNUC__) && defined (__i386__) && !defined (NOASM) && !defined (__APPLE__) && !defined (NORUSEASM)
451 void VID_BlitLinearScreen_ASM(const UINT8 *srcptr, UINT8 *destptr, INT32 width, INT32 height, size_t srcrowbytes,
452 	size_t destrowbytes);
453 #define HAVE_VIDCOPY
454 #endif
455 
CV_constextsize_OnChange(void)456 static void CV_constextsize_OnChange(void)
457 {
458 	con_recalc = true;
459 }
460 
461 
462 // --------------------------------------------------------------------------
463 // Copy a rectangular area from one bitmap to another (8bpp)
464 // --------------------------------------------------------------------------
VID_BlitLinearScreen(const UINT8 * srcptr,UINT8 * destptr,INT32 width,INT32 height,size_t srcrowbytes,size_t destrowbytes)465 void VID_BlitLinearScreen(const UINT8 *srcptr, UINT8 *destptr, INT32 width, INT32 height, size_t srcrowbytes,
466 	size_t destrowbytes)
467 {
468 #ifdef HAVE_VIDCOPY
469     VID_BlitLinearScreen_ASM(srcptr,destptr,width,height,srcrowbytes,destrowbytes);
470 #else
471 	if (srcrowbytes == destrowbytes)
472 		M_Memcpy(destptr, srcptr, srcrowbytes * height);
473 	else
474 	{
475 		while (height--)
476 		{
477 			M_Memcpy(destptr, srcptr, width);
478 
479 			destptr += destrowbytes;
480 			srcptr += srcrowbytes;
481 		}
482 	}
483 #endif
484 }
485 
486 static UINT8 hudplusalpha[11]  = { 10,  8,  6,  4,  2,  0,  0,  0,  0,  0,  0};
487 static UINT8 hudminusalpha[11] = { 10,  9,  9,  8,  8,  7,  7,  6,  6,  5,  5};
488 
489 static const UINT8 *v_colormap = NULL;
490 static const UINT8 *v_translevel = NULL;
491 
standardpdraw(const UINT8 * dest,const UINT8 * source,fixed_t ofs)492 static inline UINT8 standardpdraw(const UINT8 *dest, const UINT8 *source, fixed_t ofs)
493 {
494 	(void)dest; return source[ofs>>FRACBITS];
495 }
mappedpdraw(const UINT8 * dest,const UINT8 * source,fixed_t ofs)496 static inline UINT8 mappedpdraw(const UINT8 *dest, const UINT8 *source, fixed_t ofs)
497 {
498 	(void)dest; return *(v_colormap + source[ofs>>FRACBITS]);
499 }
translucentpdraw(const UINT8 * dest,const UINT8 * source,fixed_t ofs)500 static inline UINT8 translucentpdraw(const UINT8 *dest, const UINT8 *source, fixed_t ofs)
501 {
502 	return *(v_translevel + ((source[ofs>>FRACBITS]<<8)&0xff00) + (*dest&0xff));
503 }
transmappedpdraw(const UINT8 * dest,const UINT8 * source,fixed_t ofs)504 static inline UINT8 transmappedpdraw(const UINT8 *dest, const UINT8 *source, fixed_t ofs)
505 {
506 	return *(v_translevel + (((*(v_colormap + source[ofs>>FRACBITS]))<<8)&0xff00) + (*dest&0xff));
507 }
508 
509 // Draws a patch scaled to arbitrary size.
V_DrawStretchyFixedPatch(fixed_t x,fixed_t y,fixed_t pscale,fixed_t vscale,INT32 scrn,patch_t * patch,const UINT8 * colormap)510 void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 scrn, patch_t *patch, const UINT8 *colormap)
511 {
512 	UINT8 (*patchdrawfunc)(const UINT8*, const UINT8*, fixed_t);
513 	UINT32 alphalevel = 0;
514 
515 	fixed_t col, ofs, colfrac, rowfrac, fdup, vdup;
516 	INT32 dupx, dupy;
517 	const column_t *column;
518 	UINT8 *desttop, *dest, *deststart, *destend;
519 	const UINT8 *source, *deststop;
520 	fixed_t pwidth; // patch width
521 	fixed_t offx = 0; // x offset
522 
523 	UINT8 perplayershuffle = 0;
524 
525 	if (rendermode == render_none)
526 		return;
527 
528 #ifdef HWRENDER
529 	//if (rendermode != render_soft && !con_startup)		// Why?
530 	if (rendermode == render_opengl)
531 	{
532 		HWR_DrawStretchyFixedPatch(patch, x, y, pscale, vscale, scrn, colormap);
533 		return;
534 	}
535 #endif
536 
537 	patchdrawfunc = standardpdraw;
538 
539 	v_translevel = NULL;
540 	if ((alphalevel = ((scrn & V_ALPHAMASK) >> V_ALPHASHIFT)))
541 	{
542 		if (alphalevel == 13)
543 			alphalevel = hudminusalpha[st_translucency];
544 		else if (alphalevel == 14)
545 			alphalevel = 10 - st_translucency;
546 		else if (alphalevel == 15)
547 			alphalevel = hudplusalpha[st_translucency];
548 
549 		if (alphalevel >= 10)
550 			return; // invis
551 
552 		if (alphalevel)
553 		{
554 			v_translevel = R_GetTranslucencyTable(alphalevel);
555 			patchdrawfunc = translucentpdraw;
556 		}
557 	}
558 
559 	v_colormap = NULL;
560 	if (colormap)
561 	{
562 		v_colormap = colormap;
563 		patchdrawfunc = (v_translevel) ? transmappedpdraw : mappedpdraw;
564 	}
565 
566 	dupx = vid.dupx;
567 	dupy = vid.dupy;
568 	if (scrn & V_SCALEPATCHMASK) switch ((scrn & V_SCALEPATCHMASK) >> V_SCALEPATCHSHIFT)
569 	{
570 		case 1: // V_NOSCALEPATCH
571 			dupx = dupy = 1;
572 			break;
573 		case 2: // V_SMALLSCALEPATCH
574 			dupx = vid.smalldupx;
575 			dupy = vid.smalldupy;
576 			break;
577 		case 3: // V_MEDSCALEPATCH
578 			dupx = vid.meddupx;
579 			dupy = vid.meddupy;
580 			break;
581 		default:
582 			break;
583 	}
584 
585 	// only use one dup, to avoid stretching (har har)
586 	dupx = dupy = (dupx < dupy ? dupx : dupy);
587 	fdup = vdup = FixedMul(dupx<<FRACBITS, pscale);
588 	if (vscale != pscale)
589 		vdup = FixedMul(dupx<<FRACBITS, vscale);
590 	colfrac = FixedDiv(FRACUNIT, fdup);
591 	rowfrac = FixedDiv(FRACUNIT, vdup);
592 
593 	// So it turns out offsets aren't scaled in V_NOSCALESTART unless V_OFFSET is applied ...poo, that's terrible
594 	// For now let's just at least give V_OFFSET the ability to support V_FLIP
595 	// I'll probably make a better fix for 2.2 where I don't have to worry about breaking existing support for stuff
596 	// -- Monster Iestyn 29/10/18
597 	{
598 		fixed_t offsetx = 0, offsety = 0;
599 
600 		// left offset
601 		if (scrn & V_FLIP)
602 			offsetx = FixedMul((patch->width - patch->leftoffset)<<FRACBITS, pscale) + 1;
603 		else
604 			offsetx = FixedMul(patch->leftoffset<<FRACBITS, pscale);
605 
606 		// top offset
607 		// TODO: make some kind of vertical version of V_FLIP, maybe by deprecating V_OFFSET in future?!?
608 		offsety = FixedMul(patch->topoffset<<FRACBITS, vscale);
609 
610 		if ((scrn & (V_NOSCALESTART|V_OFFSET)) == (V_NOSCALESTART|V_OFFSET)) // Multiply by dupx/dupy for crosshairs
611 		{
612 			offsetx = FixedMul(offsetx, dupx<<FRACBITS);
613 			offsety = FixedMul(offsety, dupy<<FRACBITS);
614 		}
615 
616 		// Subtract the offsets from x/y positions
617 		x -= offsetx;
618 		y -= offsety;
619 	}
620 
621 	if (splitscreen && (scrn & V_PERPLAYER))
622 	{
623 		fixed_t adjusty = ((scrn & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)<<(FRACBITS-1);
624 		vdup >>= 1;
625 		rowfrac <<= 1;
626 		y >>= 1;
627 #ifdef QUADS
628 		if (splitscreen > 1) // 3 or 4 players
629 		{
630 			fixed_t adjustx = ((scrn & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)<<(FRACBITS-1));
631 			fdup >>= 1;
632 			colfrac <<= 1;
633 			x >>= 1;
634 			if (stplyr == &players[displayplayer])
635 			{
636 				if (!(scrn & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
637 					perplayershuffle |= 1;
638 				if (!(scrn & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
639 					perplayershuffle |= 4;
640 				scrn &= ~V_SNAPTOBOTTOM|V_SNAPTORIGHT;
641 			}
642 			else if (stplyr == &players[secondarydisplayplayer])
643 			{
644 				if (!(scrn & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
645 					perplayershuffle |= 1;
646 				if (!(scrn & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
647 					perplayershuffle |= 8;
648 				x += adjustx;
649 				scrn &= ~V_SNAPTOBOTTOM|V_SNAPTOLEFT;
650 			}
651 			else if (stplyr == &players[thirddisplayplayer])
652 			{
653 				if (!(scrn & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
654 					perplayershuffle |= 2;
655 				if (!(scrn & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
656 					perplayershuffle |= 4;
657 				y += adjusty;
658 				scrn &= ~V_SNAPTOTOP|V_SNAPTORIGHT;
659 			}
660 			else //if (stplyr == &players[fourthdisplayplayer])
661 			{
662 				if (!(scrn & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
663 					perplayershuffle |= 2;
664 				if (!(scrn & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
665 					perplayershuffle |= 8;
666 				x += adjustx;
667 				y += adjusty;
668 				scrn &= ~V_SNAPTOTOP|V_SNAPTOLEFT;
669 			}
670 		}
671 		else
672 #endif
673 		// 2 players
674 		{
675 			if (stplyr == &players[displayplayer])
676 			{
677 				if (!(scrn & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
678 					perplayershuffle = 1;
679 				scrn &= ~V_SNAPTOBOTTOM;
680 			}
681 			else //if (stplyr == &players[secondarydisplayplayer])
682 			{
683 				if (!(scrn & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
684 					perplayershuffle = 2;
685 				y += adjusty;
686 				scrn &= ~V_SNAPTOTOP;
687 			}
688 		}
689 	}
690 
691 	desttop = screens[scrn&V_PARAMMASK];
692 
693 	if (!desttop)
694 		return;
695 
696 	deststop = desttop + vid.rowbytes * vid.height;
697 
698 	if (scrn & V_NOSCALESTART)
699 	{
700 		x >>= FRACBITS;
701 		y >>= FRACBITS;
702 		desttop += (y*vid.width) + x;
703 	}
704 	else
705 	{
706 		x = FixedMul(x,dupx<<FRACBITS);
707 		y = FixedMul(y,dupy<<FRACBITS);
708 		x >>= FRACBITS;
709 		y >>= FRACBITS;
710 
711 		// Center it if necessary
712 		if (!(scrn & V_SCALEPATCHMASK))
713 		{
714 			// if it's meant to cover the whole screen, black out the rest (ONLY IF TOP LEFT ISN'T TRANSPARENT)
715 			if (x == 0 && patch->width == BASEVIDWIDTH && y == 0 && patch->height == BASEVIDHEIGHT)
716 			{
717 				column = (const column_t *)((const UINT8 *)(patch->columns) + (patch->columnofs[0]));
718 				if (!column->topdelta)
719 				{
720 					source = (const UINT8 *)(column) + 3;
721 					V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, source[0]);
722 				}
723 			}
724 
725 			if (vid.width != BASEVIDWIDTH * dupx)
726 			{
727 				// dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
728 				// so center this imaginary screen
729 				if (scrn & V_SNAPTORIGHT)
730 					x += (vid.width - (BASEVIDWIDTH * dupx));
731 				else if (!(scrn & V_SNAPTOLEFT))
732 					x += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
733 				if (perplayershuffle & 4)
734 					x -= (vid.width - (BASEVIDWIDTH * dupx)) / 4;
735 				else if (perplayershuffle & 8)
736 					x += (vid.width - (BASEVIDWIDTH * dupx)) / 4;
737 			}
738 			if (vid.height != BASEVIDHEIGHT * dupy)
739 			{
740 				// same thing here
741 				if (scrn & V_SNAPTOBOTTOM)
742 					y += (vid.height - (BASEVIDHEIGHT * dupy));
743 				else if (!(scrn & V_SNAPTOTOP))
744 					y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2;
745 				if (perplayershuffle & 1)
746 					y -= (vid.height - (BASEVIDHEIGHT * dupy)) / 4;
747 				else if (perplayershuffle & 2)
748 					y += (vid.height - (BASEVIDHEIGHT * dupy)) / 4;
749 			}
750 		}
751 
752 		desttop += (y*vid.width) + x;
753 	}
754 
755 	if (pscale != FRACUNIT) // scale width properly
756 	{
757 		pwidth = patch->width<<FRACBITS;
758 		pwidth = FixedMul(pwidth, pscale);
759 		pwidth = FixedMul(pwidth, dupx<<FRACBITS);
760 		pwidth >>= FRACBITS;
761 	}
762 	else
763 		pwidth = patch->width * dupx;
764 
765 	deststart = desttop;
766 	destend = desttop + pwidth;
767 
768 	for (col = 0; (col>>FRACBITS) < patch->width; col += colfrac, ++offx, desttop++)
769 	{
770 		INT32 topdelta, prevdelta = -1;
771 		if (scrn & V_FLIP) // offx is measured from right edge instead of left
772 		{
773 			if (x+pwidth-offx < 0) // don't draw off the left of the screen (WRAP PREVENTION)
774 				break;
775 			if (x+pwidth-offx >= vid.width) // don't draw off the right of the screen (WRAP PREVENTION)
776 				continue;
777 		}
778 		else
779 		{
780 			if (x+offx < 0) // don't draw off the left of the screen (WRAP PREVENTION)
781 				continue;
782 			if (x+offx >= vid.width) // don't draw off the right of the screen (WRAP PREVENTION)
783 				break;
784 		}
785 		column = (const column_t *)((const UINT8 *)(patch->columns) + (patch->columnofs[col>>FRACBITS]));
786 
787 		while (column->topdelta != 0xff)
788 		{
789 			topdelta = column->topdelta;
790 			if (topdelta <= prevdelta)
791 				topdelta += prevdelta;
792 			prevdelta = topdelta;
793 			source = (const UINT8 *)(column) + 3;
794 			dest = desttop;
795 			if (scrn & V_FLIP)
796 				dest = deststart + (destend - desttop);
797 			dest += FixedInt(FixedMul(topdelta<<FRACBITS,vdup))*vid.width;
798 
799 			for (ofs = 0; dest < deststop && (ofs>>FRACBITS) < column->length; ofs += rowfrac)
800 			{
801 				if (dest >= screens[scrn&V_PARAMMASK]) // don't draw off the top of the screen (CRASH PREVENTION)
802 					*dest = patchdrawfunc(dest, source, ofs);
803 				dest += vid.width;
804 			}
805 			column = (const column_t *)((const UINT8 *)column + column->length + 4);
806 		}
807 	}
808 }
809 
810 // Draws a patch cropped and scaled to arbitrary size.
V_DrawCroppedPatch(fixed_t x,fixed_t y,fixed_t pscale,INT32 scrn,patch_t * patch,fixed_t sx,fixed_t sy,fixed_t w,fixed_t h)811 void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t *patch, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h)
812 {
813 	UINT8 (*patchdrawfunc)(const UINT8*, const UINT8*, fixed_t);
814 	UINT32 alphalevel = 0;
815 	// boolean flip = false;
816 
817 	fixed_t col, ofs, colfrac, rowfrac, fdup;
818 	INT32 dupx, dupy;
819 	const column_t *column;
820 	UINT8 *desttop, *dest;
821 	const UINT8 *source, *deststop;
822 
823 	UINT8 perplayershuffle = 0;
824 
825 	if (rendermode == render_none)
826 		return;
827 
828 #ifdef HWRENDER
829 	//if (rendermode != render_soft && !con_startup)		// Not this again
830 	if (rendermode == render_opengl)
831 	{
832 		HWR_DrawCroppedPatch(patch,x,y,pscale,scrn,sx,sy,w,h);
833 		return;
834 	}
835 #endif
836 
837 	patchdrawfunc = standardpdraw;
838 
839 	v_translevel = NULL;
840 	if ((alphalevel = ((scrn & V_ALPHAMASK) >> V_ALPHASHIFT)))
841 	{
842 		if (alphalevel == 13)
843 			alphalevel = hudminusalpha[st_translucency];
844 		else if (alphalevel == 14)
845 			alphalevel = 10 - st_translucency;
846 		else if (alphalevel == 15)
847 			alphalevel = hudplusalpha[st_translucency];
848 
849 		if (alphalevel >= 10)
850 			return; // invis
851 
852 		if (alphalevel)
853 		{
854 			v_translevel = R_GetTranslucencyTable(alphalevel);
855 			patchdrawfunc = translucentpdraw;
856 		}
857 	}
858 
859 	// only use one dup, to avoid stretching (har har)
860 	dupx = dupy = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy);
861 	fdup = FixedMul(dupx<<FRACBITS, pscale);
862 	colfrac = FixedDiv(FRACUNIT, fdup);
863 	rowfrac = FixedDiv(FRACUNIT, fdup);
864 
865 	y -= FixedMul(patch->topoffset<<FRACBITS, pscale);
866 	x -= FixedMul(patch->leftoffset<<FRACBITS, pscale);
867 
868 	if (splitscreen && (scrn & V_PERPLAYER))
869 	{
870 		fixed_t adjusty = ((scrn & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)<<(FRACBITS-1);
871 		fdup >>= 1;
872 		rowfrac <<= 1;
873 		y >>= 1;
874 		sy >>= 1;
875 		h >>= 1;
876 #ifdef QUADS
877 		if (splitscreen > 1) // 3 or 4 players
878 		{
879 			fixed_t adjustx = ((scrn & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)<<(FRACBITS-1));
880 			colfrac <<= 1;
881 			x >>= 1;
882 			sx >>= 1;
883 			w >>= 1;
884 			if (stplyr == &players[displayplayer])
885 			{
886 				if (!(scrn & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
887 					perplayershuffle |= 1;
888 				if (!(scrn & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
889 					perplayershuffle |= 4;
890 				scrn &= ~V_SNAPTOBOTTOM|V_SNAPTORIGHT;
891 			}
892 			else if (stplyr == &players[secondarydisplayplayer])
893 			{
894 				if (!(scrn & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
895 					perplayershuffle |= 1;
896 				if (!(scrn & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
897 					perplayershuffle |= 8;
898 				x += adjustx;
899 				sx += adjustx;
900 				scrn &= ~V_SNAPTOBOTTOM|V_SNAPTOLEFT;
901 			}
902 			else if (stplyr == &players[thirddisplayplayer])
903 			{
904 				if (!(scrn & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
905 					perplayershuffle |= 2;
906 				if (!(scrn & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
907 					perplayershuffle |= 4;
908 				y += adjusty;
909 				sy += adjusty;
910 				scrn &= ~V_SNAPTOTOP|V_SNAPTORIGHT;
911 			}
912 			else //if (stplyr == &players[fourthdisplayplayer])
913 			{
914 				if (!(scrn & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
915 					perplayershuffle |= 2;
916 				if (!(scrn & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
917 					perplayershuffle |= 8;
918 				x += adjustx;
919 				sx += adjustx;
920 				y += adjusty;
921 				sy += adjusty;
922 				scrn &= ~V_SNAPTOTOP|V_SNAPTOLEFT;
923 			}
924 		}
925 		else
926 #endif
927 		// 2 players
928 		{
929 			if (stplyr == &players[displayplayer])
930 			{
931 				if (!(scrn & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
932 					perplayershuffle |= 1;
933 				scrn &= ~V_SNAPTOBOTTOM;
934 			}
935 			else //if (stplyr == &players[secondarydisplayplayer])
936 			{
937 				if (!(scrn & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
938 					perplayershuffle |= 2;
939 				y += adjusty;
940 				sy += adjusty;
941 				scrn &= ~V_SNAPTOTOP;
942 			}
943 		}
944 	}
945 
946 	desttop = screens[scrn&V_PARAMMASK];
947 
948 	if (!desttop)
949 		return;
950 
951 	deststop = desttop + vid.rowbytes * vid.height;
952 
953 	if (scrn & V_NOSCALESTART) {
954 		x >>= FRACBITS;
955 		y >>= FRACBITS;
956 		desttop += (y*vid.width) + x;
957 	}
958 	else
959 	{
960 		x = FixedMul(x,dupx<<FRACBITS);
961 		y = FixedMul(y,dupy<<FRACBITS);
962 		x >>= FRACBITS;
963 		y >>= FRACBITS;
964 
965 		// Center it if necessary
966 		if (!(scrn & V_SCALEPATCHMASK))
967 		{
968 			// if it's meant to cover the whole screen, black out the rest
969 			// no the patch is cropped do not do this ever
970 
971 			if (vid.width != BASEVIDWIDTH * dupx)
972 			{
973 				// dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
974 				// so center this imaginary screen
975 				if (scrn & V_SNAPTORIGHT)
976 					x += (vid.width - (BASEVIDWIDTH * dupx));
977 				else if (!(scrn & V_SNAPTOLEFT))
978 					x += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
979 				if (perplayershuffle & 4)
980 					x -= (vid.width - (BASEVIDWIDTH * dupx)) / 4;
981 				else if (perplayershuffle & 8)
982 					x += (vid.width - (BASEVIDWIDTH * dupx)) / 4;
983 			}
984 			if (vid.height != BASEVIDHEIGHT * dupy)
985 			{
986 				// same thing here
987 				if (scrn & V_SNAPTOBOTTOM)
988 					y += (vid.height - (BASEVIDHEIGHT * dupy));
989 				else if (!(scrn & V_SNAPTOTOP))
990 					y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2;
991 				if (perplayershuffle & 1)
992 					y -= (vid.height - (BASEVIDHEIGHT * dupy)) / 4;
993 				else if (perplayershuffle & 2)
994 					y += (vid.height - (BASEVIDHEIGHT * dupy)) / 4;
995 			}
996 		}
997 
998 		desttop += (y*vid.width) + x;
999 	}
1000 
1001 	for (col = sx<<FRACBITS; (col>>FRACBITS) < patch->width && ((col>>FRACBITS) - sx) < w; col += colfrac, ++x, desttop++)
1002 	{
1003 		INT32 topdelta, prevdelta = -1;
1004 		if (x < 0) // don't draw off the left of the screen (WRAP PREVENTION)
1005 			continue;
1006 		if (x >= vid.width) // don't draw off the right of the screen (WRAP PREVENTION)
1007 			break;
1008 		column = (const column_t *)((const UINT8 *)(patch->columns) + (patch->columnofs[col>>FRACBITS]));
1009 
1010 		while (column->topdelta != 0xff)
1011 		{
1012 			topdelta = column->topdelta;
1013 			if (topdelta <= prevdelta)
1014 				topdelta += prevdelta;
1015 			prevdelta = topdelta;
1016 			source = (const UINT8 *)(column) + 3;
1017 			dest = desttop;
1018 			if (topdelta-sy > 0)
1019 			{
1020 				dest += FixedInt(FixedMul((topdelta-sy)<<FRACBITS,fdup))*vid.width;
1021 				ofs = 0;
1022 			}
1023 			else
1024 				ofs = (sy-topdelta)<<FRACBITS;
1025 
1026 			for (; dest < deststop && (ofs>>FRACBITS) < column->length && (((ofs>>FRACBITS) - sy) + topdelta) < h; ofs += rowfrac)
1027 			{
1028 				if (dest >= screens[scrn&V_PARAMMASK]) // don't draw off the top of the screen (CRASH PREVENTION)
1029 					*dest = patchdrawfunc(dest, source, ofs);
1030 				dest += vid.width;
1031 			}
1032 			column = (const column_t *)((const UINT8 *)column + column->length + 4);
1033 		}
1034 	}
1035 }
1036 
1037 //
1038 // V_DrawContinueIcon
1039 // Draw a mini player!  If we can, that is.  Otherwise we draw a star.
1040 //
V_DrawContinueIcon(INT32 x,INT32 y,INT32 flags,INT32 skinnum,UINT16 skincolor)1041 void V_DrawContinueIcon(INT32 x, INT32 y, INT32 flags, INT32 skinnum, UINT16 skincolor)
1042 {
1043 	if (skinnum >= 0 && skinnum < numskins && skins[skinnum].sprites[SPR2_XTRA].numframes > XTRA_CONTINUE)
1044 	{
1045 		spritedef_t *sprdef = &skins[skinnum].sprites[SPR2_XTRA];
1046 		spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_CONTINUE];
1047 		patch_t *patch = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH);
1048 		const UINT8 *colormap = R_GetTranslationColormap(skinnum, skincolor, GTC_CACHE);
1049 
1050 		V_DrawMappedPatch(x, y, flags, patch, colormap);
1051 	}
1052 	else
1053 		V_DrawScaledPatch(x - 10, y - 14, flags, W_CachePatchName("CONTINS", PU_PATCH));
1054 }
1055 
1056 //
1057 // V_DrawBlock
1058 // Draw a linear block of pixels into the view buffer.
1059 //
V_DrawBlock(INT32 x,INT32 y,INT32 scrn,INT32 width,INT32 height,const UINT8 * src)1060 void V_DrawBlock(INT32 x, INT32 y, INT32 scrn, INT32 width, INT32 height, const UINT8 *src)
1061 {
1062 	UINT8 *dest;
1063 	const UINT8 *deststop;
1064 
1065 #ifdef RANGECHECK
1066 	if (x < 0 || x + width > vid.width || y < 0 || y + height > vid.height || (unsigned)scrn > 4)
1067 		I_Error("Bad V_DrawBlock");
1068 #endif
1069 
1070 	dest = screens[scrn] + y*vid.width + x;
1071 	deststop = screens[scrn] + vid.rowbytes * vid.height;
1072 
1073 	while (height--)
1074 	{
1075 		M_Memcpy(dest, src, width);
1076 
1077 		src += width;
1078 		dest += vid.width;
1079 		if (dest > deststop)
1080 			return;
1081 	}
1082 }
1083 
1084 static void V_BlitScaledPic(INT32 px1, INT32 py1, INT32 scrn, pic_t *pic);
1085 //  Draw a linear pic, scaled, TOTALLY CRAP CODE!!! OPTIMISE AND ASM!!
1086 //
V_DrawScaledPic(INT32 rx1,INT32 ry1,INT32 scrn,INT32 lumpnum)1087 void V_DrawScaledPic(INT32 rx1, INT32 ry1, INT32 scrn, INT32 lumpnum)
1088 {
1089 #ifdef HWRENDER
1090 	if (rendermode != render_soft)
1091 	{
1092 		HWR_DrawPic(rx1, ry1, lumpnum);
1093 		return;
1094 	}
1095 #endif
1096 
1097 	V_BlitScaledPic(rx1, ry1, scrn, W_CacheLumpNum(lumpnum, PU_CACHE));
1098 }
1099 
V_BlitScaledPic(INT32 rx1,INT32 ry1,INT32 scrn,pic_t * pic)1100 static void V_BlitScaledPic(INT32 rx1, INT32 ry1, INT32 scrn, pic_t * pic)
1101 {
1102 	INT32 dupx, dupy;
1103 	INT32 x, y;
1104 	UINT8 *src, *dest;
1105 	INT32 width, height;
1106 
1107 	width = SHORT(pic->width);
1108 	height = SHORT(pic->height);
1109 	scrn &= V_PARAMMASK;
1110 
1111 	if (pic->mode != 0)
1112 	{
1113 		CONS_Debug(DBG_RENDER, "pic mode %d not supported in Software\n", pic->mode);
1114 		return;
1115 	}
1116 
1117 	dest = screens[scrn] + max(0, ry1 * vid.width) + max(0, rx1);
1118 	// y cliping to the screen
1119 	if (ry1 + height * vid.dupy >= vid.width)
1120 		height = (vid.width - ry1) / vid.dupy - 1;
1121 	// WARNING no x clipping (not needed for the moment)
1122 
1123 	for (y = max(0, -ry1 / vid.dupy); y < height; y++)
1124 	{
1125 		for (dupy = vid.dupy; dupy; dupy--)
1126 		{
1127 			src = pic->data + y * width;
1128 			for (x = 0; x < width; x++)
1129 			{
1130 				for (dupx = vid.dupx; dupx; dupx--)
1131 					*dest++ = *src;
1132 				src++;
1133 			}
1134 			dest += vid.width - vid.dupx * width;
1135 		}
1136 	}
1137 }
1138 
1139 //
1140 // Fills a box of pixels with a single color, NOTE: scaled to screen size
1141 //
V_DrawFill(INT32 x,INT32 y,INT32 w,INT32 h,INT32 c)1142 void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c)
1143 {
1144 	UINT8 *dest;
1145 	const UINT8 *deststop;
1146 
1147 	UINT8 perplayershuffle = 0;
1148 
1149 	if (rendermode == render_none)
1150 		return;
1151 
1152 #ifdef HWRENDER
1153 	//if (rendermode != render_soft && !con_startup)		// Not this again
1154 	if (rendermode == render_opengl)
1155 	{
1156 		HWR_DrawFill(x, y, w, h, c);
1157 		return;
1158 	}
1159 #endif
1160 
1161 	if (splitscreen && (c & V_PERPLAYER))
1162 	{
1163 		fixed_t adjusty = ((c & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)>>1;
1164 		h >>= 1;
1165 		y >>= 1;
1166 #ifdef QUADS
1167 		if (splitscreen > 1) // 3 or 4 players
1168 		{
1169 			fixed_t adjustx = ((c & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)>>1;
1170 			w >>= 1;
1171 			x >>= 1;
1172 			if (stplyr == &players[displayplayer])
1173 			{
1174 				if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
1175 					perplayershuffle |= 1;
1176 				if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
1177 					perplayershuffle |= 4;
1178 				c &= ~V_SNAPTOBOTTOM|V_SNAPTORIGHT;
1179 			}
1180 			else if (stplyr == &players[secondarydisplayplayer])
1181 			{
1182 				if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
1183 					perplayershuffle |= 1;
1184 				if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
1185 					perplayershuffle |= 8;
1186 				x += adjustx;
1187 				c &= ~V_SNAPTOBOTTOM|V_SNAPTOLEFT;
1188 			}
1189 			else if (stplyr == &players[thirddisplayplayer])
1190 			{
1191 				if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
1192 					perplayershuffle |= 2;
1193 				if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
1194 					perplayershuffle |= 4;
1195 				y += adjusty;
1196 				c &= ~V_SNAPTOTOP|V_SNAPTORIGHT;
1197 			}
1198 			else //if (stplyr == &players[fourthdisplayplayer])
1199 			{
1200 				if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
1201 					perplayershuffle |= 2;
1202 				if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
1203 					perplayershuffle |= 8;
1204 				x += adjustx;
1205 				y += adjusty;
1206 				c &= ~V_SNAPTOTOP|V_SNAPTOLEFT;
1207 			}
1208 		}
1209 		else
1210 #endif
1211 		// 2 players
1212 		{
1213 			if (stplyr == &players[displayplayer])
1214 			{
1215 				if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
1216 					perplayershuffle |= 1;
1217 				c &= ~V_SNAPTOBOTTOM;
1218 			}
1219 			else //if (stplyr == &players[secondarydisplayplayer])
1220 			{
1221 				if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
1222 					perplayershuffle |= 2;
1223 				y += adjusty;
1224 				c &= ~V_SNAPTOTOP;
1225 			}
1226 		}
1227 	}
1228 
1229 	if (!(c & V_NOSCALESTART))
1230 	{
1231 		INT32 dupx = vid.dupx, dupy = vid.dupy;
1232 
1233 		if (x == 0 && y == 0 && w == BASEVIDWIDTH && h == BASEVIDHEIGHT)
1234 		{ // Clear the entire screen, from dest to deststop. Yes, this really works.
1235 			memset(screens[0], (c&255), vid.width * vid.height * vid.bpp);
1236 			return;
1237 		}
1238 
1239 		x *= dupx;
1240 		y *= dupy;
1241 		w *= dupx;
1242 		h *= dupy;
1243 
1244 		// Center it if necessary
1245 		if (vid.width != BASEVIDWIDTH * dupx)
1246 		{
1247 			// dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
1248 			// so center this imaginary screen
1249 			if (c & V_SNAPTORIGHT)
1250 				x += (vid.width - (BASEVIDWIDTH * dupx));
1251 			else if (!(c & V_SNAPTOLEFT))
1252 				x += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
1253 			if (perplayershuffle & 4)
1254 				x -= (vid.width - (BASEVIDWIDTH * dupx)) / 4;
1255 			else if (perplayershuffle & 8)
1256 				x += (vid.width - (BASEVIDWIDTH * dupx)) / 4;
1257 		}
1258 		if (vid.height != BASEVIDHEIGHT * dupy)
1259 		{
1260 			// same thing here
1261 			if (c & V_SNAPTOBOTTOM)
1262 				y += (vid.height - (BASEVIDHEIGHT * dupy));
1263 			else if (!(c & V_SNAPTOTOP))
1264 				y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2;
1265 			if (perplayershuffle & 1)
1266 				y -= (vid.height - (BASEVIDHEIGHT * dupy)) / 4;
1267 			else if (perplayershuffle & 2)
1268 				y += (vid.height - (BASEVIDHEIGHT * dupy)) / 4;
1269 		}
1270 	}
1271 
1272 	if (x >= vid.width || y >= vid.height)
1273 		return; // off the screen
1274 	if (x < 0)
1275 	{
1276 		w += x;
1277 		x = 0;
1278 	}
1279 	if (y < 0)
1280 	{
1281 		h += y;
1282 		y = 0;
1283 	}
1284 
1285 	if (w <= 0 || h <= 0)
1286 		return; // zero width/height wouldn't draw anything
1287 	if (x + w > vid.width)
1288 		w = vid.width - x;
1289 	if (y + h > vid.height)
1290 		h = vid.height - y;
1291 
1292 	dest = screens[0] + y*vid.width + x;
1293 	deststop = screens[0] + vid.rowbytes * vid.height;
1294 
1295 	c &= 255;
1296 
1297 	for (;(--h >= 0) && dest < deststop; dest += vid.width)
1298 		memset(dest, c, w * vid.bpp);
1299 }
1300 
1301 #ifdef HWRENDER
1302 // This is now a function since it's otherwise repeated 2 times and honestly looks retarded:
V_GetHWConsBackColor(void)1303 static UINT32 V_GetHWConsBackColor(void)
1304 {
1305 	UINT32 hwcolor;
1306 	switch (cons_backcolor.value)
1307 	{
1308 		case 0:		hwcolor = 0xffffff00;	break; 	// White
1309 		case 1:		hwcolor = 0x80808000;	break; 	// Black
1310 		case 2:		hwcolor = 0xdeb88700;	break;	// Sepia
1311 		case 3:		hwcolor = 0x40201000;	break; 	// Brown
1312 		case 4:		hwcolor = 0xfa807200;	break; 	// Pink
1313 		case 5:		hwcolor = 0xff69b400;	break; 	// Raspberry
1314 		case 6:		hwcolor = 0xff000000;	break; 	// Red
1315 		case 7:		hwcolor = 0xffd68300;	break;	// Creamsicle
1316 		case 8:		hwcolor = 0xff800000;	break; 	// Orange
1317 		case 9:		hwcolor = 0xdaa52000;	break; 	// Gold
1318 		case 10:	hwcolor = 0x80800000;	break; 	// Yellow
1319 		case 11:	hwcolor = 0x00ff0000;	break; 	// Emerald
1320 		case 12:	hwcolor = 0x00800000;	break; 	// Green
1321 		case 13:	hwcolor = 0x4080ff00;	break; 	// Cyan
1322 		case 14:	hwcolor = 0x4682b400;	break; 	// Steel
1323 		case 15:	hwcolor = 0x1e90ff00;	break;	// Periwinkle
1324 		case 16:	hwcolor = 0x0000ff00;	break; 	// Blue
1325 		case 17:	hwcolor = 0xff00ff00;	break; 	// Purple
1326 		case 18:	hwcolor = 0xee82ee00;	break; 	// Lavender
1327 		// Default green
1328 		default:	hwcolor = 0x00800000;	break;
1329 	}
1330 	return hwcolor;
1331 }
1332 #endif
1333 
1334 
1335 // THANK YOU MPC!!!
1336 // and thanks toaster for cleaning it up.
1337 
V_DrawFillConsoleMap(INT32 x,INT32 y,INT32 w,INT32 h,INT32 c)1338 void V_DrawFillConsoleMap(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c)
1339 {
1340 	UINT8 *dest;
1341 	const UINT8 *deststop;
1342 	INT32 u;
1343 	UINT8 *fadetable;
1344 	UINT32 alphalevel = 0;
1345 	UINT8 perplayershuffle = 0;
1346 
1347 	if (rendermode == render_none)
1348 		return;
1349 
1350 #ifdef HWRENDER
1351 	if (rendermode == render_opengl)
1352 	{
1353 		UINT32 hwcolor = V_GetHWConsBackColor();
1354 		HWR_DrawConsoleFill(x, y, w, h, c, hwcolor);	// we still use the regular color stuff but only for flags. actual draw color is "hwcolor" for this.
1355 		return;
1356 	}
1357 #endif
1358 
1359 	if ((alphalevel = ((c & V_ALPHAMASK) >> V_ALPHASHIFT)))
1360 	{
1361 		if (alphalevel == 13)
1362 			alphalevel = hudminusalpha[st_translucency];
1363 		else if (alphalevel == 14)
1364 			alphalevel = 10 - st_translucency;
1365 		else if (alphalevel == 15)
1366 			alphalevel = hudplusalpha[st_translucency];
1367 
1368 		if (alphalevel >= 10)
1369 			return; // invis
1370 	}
1371 
1372 	if (splitscreen && (c & V_PERPLAYER))
1373 	{
1374 		fixed_t adjusty = ((c & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)>>1;
1375 		h >>= 1;
1376 		y >>= 1;
1377 #ifdef QUADS
1378 		if (splitscreen > 1) // 3 or 4 players
1379 		{
1380 			fixed_t adjustx = ((c & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)>>1;
1381 			w >>= 1;
1382 			x >>= 1;
1383 			if (stplyr == &players[displayplayer])
1384 			{
1385 				if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
1386 					perplayershuffle |= 1;
1387 				if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
1388 					perplayershuffle |= 4;
1389 				c &= ~V_SNAPTOBOTTOM|V_SNAPTORIGHT;
1390 			}
1391 			else if (stplyr == &players[secondarydisplayplayer])
1392 			{
1393 				if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
1394 					perplayershuffle |= 1;
1395 				if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
1396 					perplayershuffle |= 8;
1397 				x += adjustx;
1398 				c &= ~V_SNAPTOBOTTOM|V_SNAPTOLEFT;
1399 			}
1400 			else if (stplyr == &players[thirddisplayplayer])
1401 			{
1402 				if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
1403 					perplayershuffle |= 2;
1404 				if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
1405 					perplayershuffle |= 4;
1406 				y += adjusty;
1407 				c &= ~V_SNAPTOTOP|V_SNAPTORIGHT;
1408 			}
1409 			else //if (stplyr == &players[fourthdisplayplayer])
1410 			{
1411 				if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
1412 					perplayershuffle |= 2;
1413 				if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
1414 					perplayershuffle |= 8;
1415 				x += adjustx;
1416 				y += adjusty;
1417 				c &= ~V_SNAPTOTOP|V_SNAPTOLEFT;
1418 			}
1419 		}
1420 		else
1421 #endif
1422 		// 2 players
1423 		{
1424 			if (stplyr == &players[displayplayer])
1425 			{
1426 				if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
1427 					perplayershuffle |= 1;
1428 				c &= ~V_SNAPTOBOTTOM;
1429 			}
1430 			else //if (stplyr == &players[secondarydisplayplayer])
1431 			{
1432 				if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
1433 					perplayershuffle |= 2;
1434 				y += adjusty;
1435 				c &= ~V_SNAPTOTOP;
1436 			}
1437 		}
1438 	}
1439 
1440 	if (!(c & V_NOSCALESTART))
1441 	{
1442 		INT32 dupx = vid.dupx, dupy = vid.dupy;
1443 
1444 		x *= dupx;
1445 		y *= dupy;
1446 		w *= dupx;
1447 		h *= dupy;
1448 
1449 		// Center it if necessary
1450 		if (vid.width != BASEVIDWIDTH * dupx)
1451 		{
1452 			// dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
1453 			// so center this imaginary screen
1454 			if (c & V_SNAPTORIGHT)
1455 				x += (vid.width - (BASEVIDWIDTH * dupx));
1456 			else if (!(c & V_SNAPTOLEFT))
1457 				x += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
1458 			if (perplayershuffle & 4)
1459 				x -= (vid.width - (BASEVIDWIDTH * dupx)) / 4;
1460 			else if (perplayershuffle & 8)
1461 				x += (vid.width - (BASEVIDWIDTH * dupx)) / 4;
1462 		}
1463 		if (vid.height != BASEVIDHEIGHT * dupy)
1464 		{
1465 			// same thing here
1466 			if (c & V_SNAPTOBOTTOM)
1467 				y += (vid.height - (BASEVIDHEIGHT * dupy));
1468 			else if (!(c & V_SNAPTOTOP))
1469 				y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2;
1470 			if (perplayershuffle & 1)
1471 				y -= (vid.height - (BASEVIDHEIGHT * dupy)) / 4;
1472 			else if (perplayershuffle & 2)
1473 				y += (vid.height - (BASEVIDHEIGHT * dupy)) / 4;
1474 		}
1475 	}
1476 
1477 	if (x >= vid.width || y >= vid.height)
1478 		return; // off the screen
1479 	if (x < 0) {
1480 		w += x;
1481 		x = 0;
1482 	}
1483 	if (y < 0) {
1484 		h += y;
1485 		y = 0;
1486 	}
1487 
1488 	if (w <= 0 || h <= 0)
1489 		return; // zero width/height wouldn't draw anything
1490 	if (x + w > vid.width)
1491 		w = vid.width-x;
1492 	if (y + h > vid.height)
1493 		h = vid.height-y;
1494 
1495 	dest = screens[0] + y*vid.width + x;
1496 	deststop = screens[0] + vid.rowbytes * vid.height;
1497 
1498 	c &= 255;
1499 
1500 	// Jimita (12-04-2018)
1501 	if (alphalevel)
1502 	{
1503 		fadetable = R_GetTranslucencyTable(alphalevel) + (c*256);
1504 		for (;(--h >= 0) && dest < deststop; dest += vid.width)
1505 		{
1506 			u = 0;
1507 			while (u < w)
1508 			{
1509 				dest[u] = fadetable[consolebgmap[dest[u]]];
1510 				u++;
1511 			}
1512 		}
1513 	}
1514 	else
1515 	{
1516 		for (;(--h >= 0) && dest < deststop; dest += vid.width)
1517 		{
1518 			u = 0;
1519 			while (u < w)
1520 			{
1521 				dest[u] = consolebgmap[dest[u]];
1522 				u++;
1523 			}
1524 		}
1525 	}
1526 }
1527 
1528 //
1529 // If color is 0x00 to 0xFF, draw transtable (strength range 0-9).
1530 // Else, use COLORMAP lump (strength range 0-31).
1531 // c is not color, it is for flags only. transparency flags will be ignored.
1532 // IF YOU ARE NOT CAREFUL, THIS CAN AND WILL CRASH!
1533 // I have kept the safety checks for strength out of this function;
1534 // I don't trust Lua users with it, so it doesn't matter.
1535 //
V_DrawFadeFill(INT32 x,INT32 y,INT32 w,INT32 h,INT32 c,UINT16 color,UINT8 strength)1536 void V_DrawFadeFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c, UINT16 color, UINT8 strength)
1537 {
1538 	UINT8 *dest;
1539 	const UINT8 *deststop;
1540 	INT32 u;
1541 	UINT8 *fadetable;
1542 	UINT8 perplayershuffle = 0;
1543 
1544 	if (rendermode == render_none)
1545 		return;
1546 
1547 #ifdef HWRENDER
1548 	if (rendermode == render_opengl)
1549 	{
1550 		// ughhhhh please can someone else do this? thanks ~toast 25/7/19 in 38 degrees centigrade w/o AC
1551 		HWR_DrawFadeFill(x, y, w, h, c, color, strength); // toast two days later - left above comment in 'cause it's funny
1552 		return;
1553 	}
1554 #endif
1555 
1556 	if (splitscreen && (c & V_PERPLAYER))
1557 	{
1558 		fixed_t adjusty = ((c & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)>>1;
1559 		h >>= 1;
1560 		y >>= 1;
1561 #ifdef QUADS
1562 		if (splitscreen > 1) // 3 or 4 players
1563 		{
1564 			fixed_t adjustx = ((c & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)>>1;
1565 			w >>= 1;
1566 			x >>= 1;
1567 			if (stplyr == &players[displayplayer])
1568 			{
1569 				if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
1570 					perplayershuffle |= 1;
1571 				if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
1572 					perplayershuffle |= 4;
1573 				c &= ~V_SNAPTOBOTTOM|V_SNAPTORIGHT;
1574 			}
1575 			else if (stplyr == &players[secondarydisplayplayer])
1576 			{
1577 				if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
1578 					perplayershuffle |= 1;
1579 				if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
1580 					perplayershuffle |= 8;
1581 				x += adjustx;
1582 				c &= ~V_SNAPTOBOTTOM|V_SNAPTOLEFT;
1583 			}
1584 			else if (stplyr == &players[thirddisplayplayer])
1585 			{
1586 				if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
1587 					perplayershuffle |= 2;
1588 				if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
1589 					perplayershuffle |= 4;
1590 				y += adjusty;
1591 				c &= ~V_SNAPTOTOP|V_SNAPTORIGHT;
1592 			}
1593 			else //if (stplyr == &players[fourthdisplayplayer])
1594 			{
1595 				if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
1596 					perplayershuffle |= 2;
1597 				if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT)))
1598 					perplayershuffle |= 8;
1599 				x += adjustx;
1600 				y += adjusty;
1601 				c &= ~V_SNAPTOTOP|V_SNAPTOLEFT;
1602 			}
1603 		}
1604 		else
1605 #endif
1606 		// 2 players
1607 		{
1608 			if (stplyr == &players[displayplayer])
1609 			{
1610 				if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
1611 					perplayershuffle |= 1;
1612 				c &= ~V_SNAPTOBOTTOM;
1613 			}
1614 			else //if (stplyr == &players[secondarydisplayplayer])
1615 			{
1616 				if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM)))
1617 					perplayershuffle |= 2;
1618 				y += adjusty;
1619 				c &= ~V_SNAPTOTOP;
1620 			}
1621 		}
1622 	}
1623 
1624 	if (!(c & V_NOSCALESTART))
1625 	{
1626 		INT32 dupx = vid.dupx, dupy = vid.dupy;
1627 
1628 		x *= dupx;
1629 		y *= dupy;
1630 		w *= dupx;
1631 		h *= dupy;
1632 
1633 		// Center it if necessary
1634 		if (vid.width != BASEVIDWIDTH * dupx)
1635 		{
1636 			// dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
1637 			// so center this imaginary screen
1638 			if (c & V_SNAPTORIGHT)
1639 				x += (vid.width - (BASEVIDWIDTH * dupx));
1640 			else if (!(c & V_SNAPTOLEFT))
1641 				x += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
1642 			if (perplayershuffle & 4)
1643 				x -= (vid.width - (BASEVIDWIDTH * dupx)) / 4;
1644 			else if (perplayershuffle & 8)
1645 				x += (vid.width - (BASEVIDWIDTH * dupx)) / 4;
1646 		}
1647 		if (vid.height != BASEVIDHEIGHT * dupy)
1648 		{
1649 			// same thing here
1650 			if (c & V_SNAPTOBOTTOM)
1651 				y += (vid.height - (BASEVIDHEIGHT * dupy));
1652 			else if (!(c & V_SNAPTOTOP))
1653 				y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2;
1654 			if (perplayershuffle & 1)
1655 				y -= (vid.height - (BASEVIDHEIGHT * dupy)) / 4;
1656 			else if (perplayershuffle & 2)
1657 				y += (vid.height - (BASEVIDHEIGHT * dupy)) / 4;
1658 		}
1659 	}
1660 
1661 	if (x >= vid.width || y >= vid.height)
1662 		return; // off the screen
1663 	if (x < 0) {
1664 		w += x;
1665 		x = 0;
1666 	}
1667 	if (y < 0) {
1668 		h += y;
1669 		y = 0;
1670 	}
1671 
1672 	if (w <= 0 || h <= 0)
1673 		return; // zero width/height wouldn't draw anything
1674 	if (x + w > vid.width)
1675 		w = vid.width-x;
1676 	if (y + h > vid.height)
1677 		h = vid.height-y;
1678 
1679 	dest = screens[0] + y*vid.width + x;
1680 	deststop = screens[0] + vid.rowbytes * vid.height;
1681 
1682 	c &= 255;
1683 
1684 	fadetable = ((color & 0xFF00) // Color is not palette index?
1685 		? ((UINT8 *)colormaps + strength*256) // Do COLORMAP fade.
1686 		: ((UINT8 *)R_GetTranslucencyTable((9-strength)+1) + color*256)); // Else, do TRANSMAP** fade.
1687 	for (;(--h >= 0) && dest < deststop; dest += vid.width)
1688 	{
1689 		u = 0;
1690 		while (u < w)
1691 		{
1692 			dest[u] = fadetable[dest[u]];
1693 			u++;
1694 		}
1695 	}
1696 }
1697 
1698 //
1699 // Fills a box of pixels using a flat texture as a pattern, scaled to screen size.
1700 //
V_DrawFlatFill(INT32 x,INT32 y,INT32 w,INT32 h,lumpnum_t flatnum)1701 void V_DrawFlatFill(INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatnum)
1702 {
1703 	INT32 u, v, dupx, dupy;
1704 	fixed_t dx, dy, xfrac, yfrac;
1705 	const UINT8 *src, *deststop;
1706 	UINT8 *flat, *dest;
1707 	size_t size, lflatsize, flatshift;
1708 
1709 #ifdef HWRENDER
1710 	if (rendermode == render_opengl)
1711 	{
1712 		HWR_DrawFlatFill(x, y, w, h, flatnum);
1713 		return;
1714 	}
1715 #endif
1716 
1717 	size = W_LumpLength(flatnum);
1718 
1719 	switch (size)
1720 	{
1721 		case 4194304: // 2048x2048 lump
1722 			lflatsize = 2048;
1723 			flatshift = 10;
1724 			break;
1725 		case 1048576: // 1024x1024 lump
1726 			lflatsize = 1024;
1727 			flatshift = 9;
1728 			break;
1729 		case 262144:// 512x512 lump
1730 			lflatsize = 512;
1731 			flatshift = 8;
1732 			break;
1733 		case 65536: // 256x256 lump
1734 			lflatsize = 256;
1735 			flatshift = 7;
1736 			break;
1737 		case 16384: // 128x128 lump
1738 			lflatsize = 128;
1739 			flatshift = 7;
1740 			break;
1741 		case 1024: // 32x32 lump
1742 			lflatsize = 32;
1743 			flatshift = 5;
1744 			break;
1745 		default: // 64x64 lump
1746 			lflatsize = 64;
1747 			flatshift = 6;
1748 			break;
1749 	}
1750 
1751 	flat = W_CacheLumpNum(flatnum, PU_CACHE);
1752 
1753 	dupx = dupy = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy);
1754 
1755 	dest = screens[0] + y*dupy*vid.width + x*dupx;
1756 	deststop = screens[0] + vid.rowbytes * vid.height;
1757 
1758 	// from V_DrawScaledPatch
1759 	if (vid.width != BASEVIDWIDTH * dupx)
1760 	{
1761 		// dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
1762 		// so center this imaginary screen
1763 		dest += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
1764 	}
1765 	if (vid.height != BASEVIDHEIGHT * dupy)
1766 	{
1767 		// same thing here
1768 		dest += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width / 2;
1769 	}
1770 
1771 	w *= dupx;
1772 	h *= dupy;
1773 
1774 	dx = FixedDiv(FRACUNIT, dupx<<(FRACBITS-2));
1775 	dy = FixedDiv(FRACUNIT, dupy<<(FRACBITS-2));
1776 
1777 	yfrac = 0;
1778 	for (v = 0; v < h; v++, dest += vid.width)
1779 	{
1780 		xfrac = 0;
1781 		src = flat + (((yfrac>>FRACBITS) & (lflatsize - 1)) << flatshift);
1782 		for (u = 0; u < w; u++)
1783 		{
1784 			if (&dest[u] > deststop)
1785 				return;
1786 			dest[u] = src[(xfrac>>FRACBITS)&(lflatsize-1)];
1787 			xfrac += dx;
1788 		}
1789 		yfrac += dy;
1790 	}
1791 }
1792 
1793 //
1794 // V_DrawPatchFill
1795 //
V_DrawPatchFill(patch_t * pat)1796 void V_DrawPatchFill(patch_t *pat)
1797 {
1798 	INT32 dupz = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy);
1799 	INT32 x, y, pw = pat->width * dupz, ph = pat->height * dupz;
1800 
1801 	for (x = 0; x < vid.width; x += pw)
1802 	{
1803 		for (y = 0; y < vid.height; y += ph)
1804 			V_DrawScaledPatch(x, y, V_NOSCALESTART, pat);
1805 	}
1806 }
1807 
1808 //
1809 // Fade all the screen buffer, so that the menu is more readable,
1810 // especially now that we use the small hufont in the menus...
1811 // If color is 0x00 to 0xFF, draw transtable (strength range 0-9).
1812 // Else, use COLORMAP lump (strength range 0-31).
1813 // IF YOU ARE NOT CAREFUL, THIS CAN AND WILL CRASH!
1814 // I have kept the safety checks out of this function;
1815 // the v.fadeScreen Lua interface handles those.
1816 //
V_DrawFadeScreen(UINT16 color,UINT8 strength)1817 void V_DrawFadeScreen(UINT16 color, UINT8 strength)
1818 {
1819 #ifdef HWRENDER
1820 	if (rendermode == render_opengl)
1821 	{
1822 		HWR_FadeScreenMenuBack(color, strength);
1823 		return;
1824 	}
1825 #endif
1826 
1827 	{
1828 		const UINT8 *fadetable = ((color & 0xFF00) // Color is not palette index?
1829 		? ((UINT8 *)(((color & 0x0F00) == 0x0A00) ? fadecolormap // Do fadecolormap fade.
1830 		: (((color & 0x0F00) == 0x0B00) ? fadecolormap + (256 * FADECOLORMAPROWS) // Do white fadecolormap fade.
1831 		: colormaps)) + strength*256) // Do COLORMAP fade.
1832 		: ((UINT8 *)R_GetTranslucencyTable((9-strength)+1) + color*256)); // Else, do TRANSMAP** fade.
1833 		const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
1834 		UINT8 *buf = screens[0];
1835 
1836 		// heavily simplified -- we don't need to know x or y
1837 		// position when we're doing a full screen fade
1838 		for (; buf < deststop; ++buf)
1839 			*buf = fadetable[*buf];
1840 	}
1841 }
1842 
1843 // Simple translucency with one color, over a set number of lines starting from the top.
V_DrawFadeConsBack(INT32 plines)1844 void V_DrawFadeConsBack(INT32 plines)
1845 {
1846 	UINT8 *deststop, *buf;
1847 
1848 #ifdef HWRENDER // not win32 only 19990829 by Kin
1849 	if (rendermode == render_opengl)
1850 	{
1851 		UINT32 hwcolor = V_GetHWConsBackColor();
1852 		HWR_DrawConsoleBack(hwcolor, plines);
1853 		return;
1854 	}
1855 #endif
1856 
1857 	// heavily simplified -- we don't need to know x or y position,
1858 	// just the stop position
1859 	deststop = screens[0] + vid.rowbytes * min(plines, vid.height);
1860 	for (buf = screens[0]; buf < deststop; ++buf)
1861 		*buf = consolebgmap[*buf];
1862 }
1863 
1864 // Very similar to F_DrawFadeConsBack, except we draw from the middle(-ish) of the screen to the bottom.
V_DrawPromptBack(INT32 boxheight,INT32 color)1865 void V_DrawPromptBack(INT32 boxheight, INT32 color)
1866 {
1867 	UINT8 *deststop, *buf;
1868 
1869 	if (color >= 256 && color < 512)
1870 	{
1871 		if (boxheight < 0)
1872 			boxheight = -boxheight;
1873 		else // 4 lines of space plus gaps between and some leeway
1874 			boxheight = ((boxheight * 4) + (boxheight/2)*5);
1875 		V_DrawFill((BASEVIDWIDTH-(vid.width/vid.dupx))/2, BASEVIDHEIGHT-boxheight, (vid.width/vid.dupx),boxheight, (color-256)|V_SNAPTOBOTTOM);
1876 		return;
1877 	}
1878 
1879 	boxheight *= vid.dupy;
1880 
1881 	if (color == INT32_MAX)
1882 		color = cons_backcolor.value;
1883 
1884 #ifdef HWRENDER
1885 	if (rendermode == render_opengl)
1886 	{
1887 		UINT32 hwcolor;
1888 		switch (color)
1889 		{
1890 			case 0:		hwcolor = 0xffffff00;	break; 	// White
1891 			case 1:		hwcolor = 0x00000000;	break; 	// Black // Note this is different from V_DrawFadeConsBack
1892 			case 2:		hwcolor = 0xdeb88700;	break;	// Sepia
1893 			case 3:		hwcolor = 0x40201000;	break; 	// Brown
1894 			case 4:		hwcolor = 0xfa807200;	break; 	// Pink
1895 			case 5:		hwcolor = 0xff69b400;	break; 	// Raspberry
1896 			case 6:		hwcolor = 0xff000000;	break; 	// Red
1897 			case 7:		hwcolor = 0xffd68300;	break;	// Creamsicle
1898 			case 8:		hwcolor = 0xff800000;	break; 	// Orange
1899 			case 9:		hwcolor = 0xdaa52000;	break; 	// Gold
1900 			case 10:	hwcolor = 0x80800000;	break; 	// Yellow
1901 			case 11:	hwcolor = 0x00ff0000;	break; 	// Emerald
1902 			case 12:	hwcolor = 0x00800000;	break; 	// Green
1903 			case 13:	hwcolor = 0x4080ff00;	break; 	// Cyan
1904 			case 14:	hwcolor = 0x4682b400;	break; 	// Steel
1905 			case 15:	hwcolor = 0x1e90ff00;	break;	// Periwinkle
1906 			case 16:	hwcolor = 0x0000ff00;	break; 	// Blue
1907 			case 17:	hwcolor = 0xff00ff00;	break; 	// Purple
1908 			case 18:	hwcolor = 0xee82ee00;	break; 	// Lavender
1909 			// Default green
1910 			default:	hwcolor = 0x00800000;	break;
1911 		}
1912 		HWR_DrawTutorialBack(hwcolor, boxheight);
1913 		return;
1914 	}
1915 #endif
1916 
1917 	CON_SetupBackColormapEx(color, true);
1918 
1919 	// heavily simplified -- we don't need to know x or y position,
1920 	// just the start and stop positions
1921 	buf = deststop = screens[0] + vid.rowbytes * vid.height;
1922 	if (boxheight < 0)
1923 		buf += vid.rowbytes * boxheight;
1924 	else // 4 lines of space plus gaps between and some leeway
1925 		buf -= vid.rowbytes * ((boxheight * 4) + (boxheight/2)*5);
1926 	for (; buf < deststop; ++buf)
1927 		*buf = promptbgmap[*buf];
1928 }
1929 
1930 // Gets string colormap, used for 0x80 color codes
1931 //
V_GetStringColormap(INT32 colorflags)1932 UINT8 *V_GetStringColormap(INT32 colorflags)
1933 {
1934 	switch ((colorflags & V_CHARCOLORMASK) >> V_CHARCOLORSHIFT)
1935 	{
1936 	case  1: // 0x81, magenta
1937 		return magentamap;
1938 	case  2: // 0x82, yellow
1939 		return yellowmap;
1940 	case  3: // 0x83, lgreen
1941 		return lgreenmap;
1942 	case  4: // 0x84, blue
1943 		return bluemap;
1944 	case  5: // 0x85, red
1945 		return redmap;
1946 	case  6: // 0x86, gray
1947 		return graymap;
1948 	case  7: // 0x87, orange
1949 		return orangemap;
1950 	case  8: // 0x88, sky
1951 		return skymap;
1952 	case  9: // 0x89, purple
1953 		return purplemap;
1954 	case 10: // 0x8A, aqua
1955 		return aquamap;
1956 	case 11: // 0x8B, peridot
1957 		return peridotmap;
1958 	case 12: // 0x8C, azure
1959 		return azuremap;
1960 	case 13: // 0x8D, brown
1961 		return brownmap;
1962 	case 14: // 0x8E, rosy
1963 		return rosymap;
1964 	case 15: // 0x8F, invert
1965 		return invertmap;
1966 	default: // reset
1967 		return NULL;
1968 	}
1969 }
1970 
1971 // Writes a single character (draw WHITE if bit 7 set)
1972 //
V_DrawCharacter(INT32 x,INT32 y,INT32 c,boolean lowercaseallowed)1973 void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed)
1974 {
1975 	INT32 w, flags;
1976 	const UINT8 *colormap = V_GetStringColormap(c);
1977 
1978 	flags = c & ~(V_CHARCOLORMASK | V_PARAMMASK);
1979 	c &= 0x7f;
1980 	if (lowercaseallowed)
1981 		c -= HU_FONTSTART;
1982 	else
1983 		c = toupper(c) - HU_FONTSTART;
1984 	if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
1985 		return;
1986 
1987 	w = hu_font[c]->width;
1988 	if (x + w > vid.width)
1989 		return;
1990 
1991 	if (colormap != NULL)
1992 		V_DrawMappedPatch(x, y, flags, hu_font[c], colormap);
1993 	else
1994 		V_DrawScaledPatch(x, y, flags, hu_font[c]);
1995 }
1996 
1997 // Writes a single character for the chat. (draw WHITE if bit 7 set)
1998 // Essentially the same as the above but it's small or big depending on what resolution you've chosen to huge..
1999 //
V_DrawChatCharacter(INT32 x,INT32 y,INT32 c,boolean lowercaseallowed,UINT8 * colormap)2000 void V_DrawChatCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed, UINT8 *colormap)
2001 {
2002 	INT32 w, flags;
2003 	//const UINT8 *colormap = V_GetStringColormap(c);
2004 
2005 	flags = c & ~(V_CHARCOLORMASK | V_PARAMMASK);
2006 	c &= 0x7f;
2007 	if (lowercaseallowed)
2008 		c -= HU_FONTSTART;
2009 	else
2010 		c = toupper(c) - HU_FONTSTART;
2011 	if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
2012 		return;
2013 
2014 	w = (vid.width < 640 ) ? ((hu_font[c]->width / 2)) : (hu_font[c]->width);	// use normal sized characters if we're using a terribly low resolution.
2015 	if (x + w > vid.width)
2016 		return;
2017 
2018 	V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, (vid.width < 640) ? (FRACUNIT) : (FRACUNIT/2), flags, hu_font[c], colormap);
2019 
2020 
2021 }
2022 
2023 // Precompile a wordwrapped string to any given width.
2024 // This is a muuuch better method than V_WORDWRAP.
V_WordWrap(INT32 x,INT32 w,INT32 option,const char * string)2025 char *V_WordWrap(INT32 x, INT32 w, INT32 option, const char *string)
2026 {
2027 	int c;
2028 	size_t chw, i, lastusablespace = 0;
2029 	size_t slen;
2030 	char *newstring = Z_StrDup(string);
2031 	INT32 spacewidth = 4, charwidth = 0;
2032 
2033 	slen = strlen(string);
2034 
2035 	if (w == 0)
2036 		w = BASEVIDWIDTH;
2037 	w -= x;
2038 	x = 0;
2039 
2040 	switch (option & V_SPACINGMASK)
2041 	{
2042 		case V_MONOSPACE:
2043 			spacewidth = 8;
2044 			/* FALLTHRU */
2045 		case V_OLDSPACING:
2046 			charwidth = 8;
2047 			break;
2048 		case V_6WIDTHSPACE:
2049 			spacewidth = 6;
2050 		default:
2051 			break;
2052 	}
2053 
2054 	for (i = 0; i < slen; ++i)
2055 	{
2056 		c = newstring[i];
2057 		if ((UINT8)c & 0x80) //color parsing! -Inuyasha 2.16.09
2058 			continue;
2059 
2060 		if (c == '\n')
2061 		{
2062 			x = 0;
2063 			lastusablespace = 0;
2064 			continue;
2065 		}
2066 
2067 		if (!(option & V_ALLOWLOWERCASE))
2068 			c = toupper(c);
2069 		c -= HU_FONTSTART;
2070 
2071 		if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
2072 		{
2073 			chw = spacewidth;
2074 			lastusablespace = i;
2075 		}
2076 		else
2077 			chw = (charwidth ? charwidth : hu_font[c]->width);
2078 
2079 		x += chw;
2080 
2081 		if (lastusablespace != 0 && x > w)
2082 		{
2083 			newstring[lastusablespace] = '\n';
2084 			i = lastusablespace;
2085 			lastusablespace = 0;
2086 			x = 0;
2087 		}
2088 	}
2089 	return newstring;
2090 }
2091 
2092 //
2093 // Write a string using the hu_font
2094 // NOTE: the text is centered for screens larger than the base width
2095 //
V_DrawString(INT32 x,INT32 y,INT32 option,const char * string)2096 void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string)
2097 {
2098 	INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, center = 0, left = 0;
2099 	const char *ch = string;
2100 	INT32 charflags = (option & V_CHARCOLORMASK);
2101 	const UINT8 *colormap = NULL;
2102 	INT32 spacewidth = 4, charwidth = 0;
2103 
2104 	INT32 lowercase = (option & V_ALLOWLOWERCASE);
2105 	option &= ~V_FLIP; // which is also shared with V_ALLOWLOWERCASE...
2106 
2107 	if (option & V_NOSCALESTART)
2108 	{
2109 		dupx = vid.dupx;
2110 		dupy = vid.dupy;
2111 		scrwidth = vid.width;
2112 	}
2113 	else
2114 	{
2115 		dupx = dupy = 1;
2116 		scrwidth = vid.width/vid.dupx;
2117 		left = (scrwidth - BASEVIDWIDTH)/2;
2118 		scrwidth -= left;
2119 	}
2120 
2121 	if (option & V_NOSCALEPATCH)
2122 		scrwidth *= vid.dupx;
2123 
2124 	switch (option & V_SPACINGMASK)
2125 	{
2126 		case V_MONOSPACE:
2127 			spacewidth = 8;
2128 			/* FALLTHRU */
2129 		case V_OLDSPACING:
2130 			charwidth = 8;
2131 			break;
2132 		case V_6WIDTHSPACE:
2133 			spacewidth = 6;
2134 		default:
2135 			break;
2136 	}
2137 
2138 	for (;;ch++)
2139 	{
2140 		if (!*ch)
2141 			break;
2142 		if (*ch & 0x80) //color parsing -x 2.16.09
2143 		{
2144 			// manually set flags override color codes
2145 			if (!(option & V_CHARCOLORMASK))
2146 				charflags = ((*ch & 0x7f) << V_CHARCOLORSHIFT) & V_CHARCOLORMASK;
2147 			continue;
2148 		}
2149 		if (*ch == '\n')
2150 		{
2151 			cx = x;
2152 
2153 			if (option & V_RETURN8)
2154 				cy += 8*dupy;
2155 			else
2156 				cy += 12*dupy;
2157 
2158 			continue;
2159 		}
2160 
2161 		c = *ch;
2162 		if (!lowercase)
2163 			c = toupper(c);
2164 		c -= HU_FONTSTART;
2165 
2166 		// character does not exist or is a space
2167 		if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
2168 		{
2169 			cx += spacewidth * dupx;
2170 			continue;
2171 		}
2172 
2173 		if (charwidth)
2174 		{
2175 			w = charwidth * dupx;
2176 			center = w/2 - hu_font[c]->width*dupx/2;
2177 		}
2178 		else
2179 			w = hu_font[c]->width * dupx;
2180 
2181 		if (cx > scrwidth)
2182 			continue;
2183 		if (cx+left + w < 0) //left boundary check
2184 		{
2185 			cx += w;
2186 			continue;
2187 		}
2188 
2189 		colormap = V_GetStringColormap(charflags);
2190 		V_DrawFixedPatch((cx + center)<<FRACBITS, cy<<FRACBITS, FRACUNIT, option, hu_font[c], colormap);
2191 
2192 		cx += w;
2193 	}
2194 }
2195 
V_DrawCenteredString(INT32 x,INT32 y,INT32 option,const char * string)2196 void V_DrawCenteredString(INT32 x, INT32 y, INT32 option, const char *string)
2197 {
2198 	x -= V_StringWidth(string, option)/2;
2199 	V_DrawString(x, y, option, string);
2200 }
2201 
V_DrawRightAlignedString(INT32 x,INT32 y,INT32 option,const char * string)2202 void V_DrawRightAlignedString(INT32 x, INT32 y, INT32 option, const char *string)
2203 {
2204 	x -= V_StringWidth(string, option);
2205 	V_DrawString(x, y, option, string);
2206 }
2207 
2208 //
2209 // Write a string using the hu_font, 0.5x scale
2210 // NOTE: the text is centered for screens larger than the base width
2211 //
V_DrawSmallString(INT32 x,INT32 y,INT32 option,const char * string)2212 void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string)
2213 {
2214 	INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, center = 0, left = 0;
2215 	const char *ch = string;
2216 	INT32 charflags = 0;
2217 	const UINT8 *colormap = NULL;
2218 	INT32 spacewidth = 2, charwidth = 0;
2219 
2220 	INT32 lowercase = (option & V_ALLOWLOWERCASE);
2221 	option &= ~V_FLIP; // which is also shared with V_ALLOWLOWERCASE...
2222 
2223 	if (option & V_NOSCALESTART)
2224 	{
2225 		dupx = vid.dupx;
2226 		dupy = vid.dupy;
2227 		scrwidth = vid.width;
2228 	}
2229 	else
2230 	{
2231 		dupx = dupy = 1;
2232 		scrwidth = vid.width/vid.dupx;
2233 		left = (scrwidth - BASEVIDWIDTH)/2;
2234 		scrwidth -= left;
2235 	}
2236 
2237 	if (option & V_NOSCALEPATCH)
2238 		scrwidth *= vid.dupx;
2239 
2240 	charflags = (option & V_CHARCOLORMASK);
2241 
2242 	switch (option & V_SPACINGMASK)
2243 	{
2244 		case V_MONOSPACE:
2245 			spacewidth = 4;
2246 			/* FALLTHRU */
2247 		case V_OLDSPACING:
2248 			charwidth = 4;
2249 			break;
2250 		case V_6WIDTHSPACE:
2251 			spacewidth = 3;
2252 		default:
2253 			break;
2254 	}
2255 
2256 	for (;;ch++)
2257 	{
2258 		if (!*ch)
2259 			break;
2260 		if (*ch & 0x80) //color parsing -x 2.16.09
2261 		{
2262 			// manually set flags override color codes
2263 			if (!(option & V_CHARCOLORMASK))
2264 				charflags = ((*ch & 0x7f) << V_CHARCOLORSHIFT) & V_CHARCOLORMASK;
2265 			continue;
2266 		}
2267 		if (*ch == '\n')
2268 		{
2269 			cx = x;
2270 
2271 			if (option & V_RETURN8)
2272 				cy += 4*dupy;
2273 			else
2274 				cy += 6*dupy;
2275 
2276 			continue;
2277 		}
2278 
2279 		c = *ch;
2280 		if (!lowercase)
2281 			c = toupper(c);
2282 		c -= HU_FONTSTART;
2283 
2284 		if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
2285 		{
2286 			cx += spacewidth * dupx;
2287 			continue;
2288 		}
2289 
2290 		if (charwidth)
2291 		{
2292 			w = charwidth * dupx;
2293 			center = w/2 - hu_font[c]->width*dupx/4;
2294 		}
2295 		else
2296 			w = hu_font[c]->width * dupx / 2;
2297 
2298 		if (cx > scrwidth)
2299 			continue;
2300 		if (cx+left + w < 0) //left boundary check
2301 		{
2302 			cx += w;
2303 			continue;
2304 		}
2305 
2306 		colormap = V_GetStringColormap(charflags);
2307 		V_DrawFixedPatch((cx + center)<<FRACBITS, cy<<FRACBITS, FRACUNIT/2, option, hu_font[c], colormap);
2308 
2309 		cx += w;
2310 	}
2311 }
2312 
V_DrawCenteredSmallString(INT32 x,INT32 y,INT32 option,const char * string)2313 void V_DrawCenteredSmallString(INT32 x, INT32 y, INT32 option, const char *string)
2314 {
2315 	x -= V_SmallStringWidth(string, option)/2;
2316 	V_DrawSmallString(x, y, option, string);
2317 }
2318 
2319 
V_DrawRightAlignedSmallString(INT32 x,INT32 y,INT32 option,const char * string)2320 void V_DrawRightAlignedSmallString(INT32 x, INT32 y, INT32 option, const char *string)
2321 {
2322 	x -= V_SmallStringWidth(string, option);
2323 	V_DrawSmallString(x, y, option, string);
2324 }
2325 
2326 //
2327 // Write a string using the tny_font
2328 // NOTE: the text is centered for screens larger than the base width
2329 //
V_DrawThinString(INT32 x,INT32 y,INT32 option,const char * string)2330 void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string)
2331 {
2332 	INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, left = 0;
2333 	const char *ch = string;
2334 	INT32 charflags = 0;
2335 	const UINT8 *colormap = NULL;
2336 	INT32 spacewidth = 2, charwidth = 0;
2337 
2338 	INT32 lowercase = (option & V_ALLOWLOWERCASE);
2339 	option &= ~V_FLIP; // which is also shared with V_ALLOWLOWERCASE...
2340 
2341 	if (option & V_NOSCALESTART)
2342 	{
2343 		dupx = vid.dupx;
2344 		dupy = vid.dupy;
2345 		scrwidth = vid.width;
2346 	}
2347 	else
2348 	{
2349 		dupx = dupy = 1;
2350 		scrwidth = vid.width/vid.dupx;
2351 		left = (scrwidth - BASEVIDWIDTH)/2;
2352 		scrwidth -= left;
2353 	}
2354 
2355 	if (option & V_NOSCALEPATCH)
2356 		scrwidth *= vid.dupx;
2357 
2358 	charflags = (option & V_CHARCOLORMASK);
2359 
2360 	switch (option & V_SPACINGMASK)
2361 	{
2362 		case V_MONOSPACE:
2363 			spacewidth = 5;
2364 			/* FALLTHRU */
2365 		case V_OLDSPACING:
2366 			charwidth = 5;
2367 			break;
2368 		case V_6WIDTHSPACE:
2369 			spacewidth = 3;
2370 		default:
2371 			break;
2372 	}
2373 
2374 	for (;;ch++)
2375 	{
2376 		if (!*ch)
2377 			break;
2378 		if (*ch & 0x80) //color parsing -x 2.16.09
2379 		{
2380 			// manually set flags override color codes
2381 			if (!(option & V_CHARCOLORMASK))
2382 				charflags = ((*ch & 0x7f) << V_CHARCOLORSHIFT) & V_CHARCOLORMASK;
2383 			continue;
2384 		}
2385 		if (*ch == '\n')
2386 		{
2387 			cx = x;
2388 
2389 			if (option & V_RETURN8)
2390 				cy += 8*dupy;
2391 			else
2392 				cy += 12*dupy;
2393 
2394 			continue;
2395 		}
2396 
2397 		c = *ch;
2398 		if (!lowercase || !tny_font[c-HU_FONTSTART])
2399 			c = toupper(c);
2400 		c -= HU_FONTSTART;
2401 
2402 		if (c < 0 || c >= HU_FONTSIZE || !tny_font[c])
2403 		{
2404 			cx += spacewidth * dupx;
2405 			continue;
2406 		}
2407 
2408 		if (charwidth)
2409 			w = charwidth * dupx;
2410 		else
2411 			w = tny_font[c]->width * dupx;
2412 
2413 		if (cx > scrwidth)
2414 			continue;
2415 		if (cx+left + w < 0) //left boundary check
2416 		{
2417 			cx += w;
2418 			continue;
2419 		}
2420 
2421 		colormap = V_GetStringColormap(charflags);
2422 		V_DrawFixedPatch(cx<<FRACBITS, cy<<FRACBITS, FRACUNIT, option, tny_font[c], colormap);
2423 
2424 		cx += w;
2425 	}
2426 }
2427 
V_DrawCenteredThinString(INT32 x,INT32 y,INT32 option,const char * string)2428 void V_DrawCenteredThinString(INT32 x, INT32 y, INT32 option, const char *string)
2429 {
2430 	x -= V_ThinStringWidth(string, option)/2;
2431 	V_DrawThinString(x, y, option, string);
2432 }
2433 
V_DrawRightAlignedThinString(INT32 x,INT32 y,INT32 option,const char * string)2434 void V_DrawRightAlignedThinString(INT32 x, INT32 y, INT32 option, const char *string)
2435 {
2436 	x -= V_ThinStringWidth(string, option);
2437 	V_DrawThinString(x, y, option, string);
2438 }
2439 
2440 //
2441 // Write a string using the tny_font, 0.5x scale
2442 // NOTE: the text is centered for screens larger than the base width
2443 //
2444 // Literally a wrapper. ~Golden
V_DrawSmallThinString(INT32 x,INT32 y,INT32 option,const char * string)2445 void V_DrawSmallThinString(INT32 x, INT32 y, INT32 option, const char *string)
2446 {
2447 	x <<= FRACBITS;
2448 	y <<= FRACBITS;
2449 	V_DrawSmallThinStringAtFixed((fixed_t)x, (fixed_t)y, option, string);
2450 }
2451 
V_DrawCenteredSmallThinString(INT32 x,INT32 y,INT32 option,const char * string)2452 void V_DrawCenteredSmallThinString(INT32 x, INT32 y, INT32 option, const char *string)
2453 {
2454 	x <<= FRACBITS;
2455 	y <<= FRACBITS;
2456 	V_DrawCenteredSmallThinStringAtFixed((fixed_t)x, (fixed_t)y, option, string);
2457 }
2458 
V_DrawRightAlignedSmallThinString(INT32 x,INT32 y,INT32 option,const char * string)2459 void V_DrawRightAlignedSmallThinString(INT32 x, INT32 y, INT32 option, const char *string)
2460 {
2461 	x <<= FRACBITS;
2462 	y <<= FRACBITS;
2463 	V_DrawRightAlignedSmallThinStringAtFixed((fixed_t)x, (fixed_t)y, option, string);
2464 }
2465 
2466 // Draws a string at a fixed_t location.
V_DrawStringAtFixed(fixed_t x,fixed_t y,INT32 option,const char * string)2467 void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
2468 {
2469 	fixed_t cx = x, cy = y;
2470 	INT32 w, c, dupx, dupy, scrwidth, center = 0, left = 0;
2471 	const char *ch = string;
2472 	INT32 charflags = 0;
2473 	const UINT8 *colormap = NULL;
2474 	INT32 spacewidth = 4, charwidth = 0;
2475 
2476 	INT32 lowercase = (option & V_ALLOWLOWERCASE);
2477 	option &= ~V_FLIP; // which is also shared with V_ALLOWLOWERCASE...
2478 
2479 	if (option & V_NOSCALESTART)
2480 	{
2481 		dupx = vid.dupx;
2482 		dupy = vid.dupy;
2483 		scrwidth = vid.width;
2484 	}
2485 	else
2486 	{
2487 		dupx = dupy = 1;
2488 		scrwidth = vid.width/vid.dupx;
2489 		left = (scrwidth - BASEVIDWIDTH)/2;
2490 		scrwidth -= left;
2491 	}
2492 
2493 	if (option & V_NOSCALEPATCH)
2494 		scrwidth *= vid.dupx;
2495 
2496 	charflags = (option & V_CHARCOLORMASK);
2497 
2498 	switch (option & V_SPACINGMASK)
2499 	{
2500 		case V_MONOSPACE:
2501 			spacewidth = 8;
2502 			/* FALLTHRU */
2503 		case V_OLDSPACING:
2504 			charwidth = 8;
2505 			break;
2506 		case V_6WIDTHSPACE:
2507 			spacewidth = 6;
2508 		default:
2509 			break;
2510 	}
2511 
2512 	for (;;ch++)
2513 	{
2514 		if (!*ch)
2515 			break;
2516 		if (*ch & 0x80) //color ignoring
2517 		{
2518 			// manually set flags override color codes
2519 			if (!(option & V_CHARCOLORMASK))
2520 				charflags = ((*ch & 0x7f) << V_CHARCOLORSHIFT) & V_CHARCOLORMASK;
2521 			continue;
2522 		}
2523 		if (*ch == '\n')
2524 		{
2525 			cx = x;
2526 
2527 			if (option & V_RETURN8)
2528 				cy += (8*dupy)<<FRACBITS;
2529 			else
2530 				cy += (12*dupy)<<FRACBITS;
2531 
2532 			continue;
2533 		}
2534 
2535 		c = *ch;
2536 		if (!lowercase)
2537 			c = toupper(c);
2538 		c -= HU_FONTSTART;
2539 
2540 		// character does not exist or is a space
2541 		if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
2542 		{
2543 			cx += (spacewidth * dupx)<<FRACBITS;
2544 			continue;
2545 		}
2546 
2547 		if (charwidth)
2548 		{
2549 			w = charwidth * dupx;
2550 			center = w/2 - hu_font[c]->width*(dupx/2);
2551 		}
2552 		else
2553 			w = hu_font[c]->width * dupx;
2554 
2555 		if ((cx>>FRACBITS) > scrwidth)
2556 			continue;
2557 		if ((cx>>FRACBITS)+left + w < 0) //left boundary check
2558 		{
2559 			cx += w<<FRACBITS;
2560 			continue;
2561 		}
2562 
2563 		colormap = V_GetStringColormap(charflags);
2564 		V_DrawFixedPatch(cx + (center<<FRACBITS), cy, FRACUNIT, option, hu_font[c], colormap);
2565 
2566 		cx += w<<FRACBITS;
2567 	}
2568 }
2569 
V_DrawCenteredStringAtFixed(fixed_t x,fixed_t y,INT32 option,const char * string)2570 void V_DrawCenteredStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
2571 {
2572 	x -= (V_StringWidth(string, option) / 2)<<FRACBITS;
2573 	V_DrawStringAtFixed(x, y, option, string);
2574 }
2575 
V_DrawRightAlignedStringAtFixed(fixed_t x,fixed_t y,INT32 option,const char * string)2576 void V_DrawRightAlignedStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
2577 {
2578 	x -= V_StringWidth(string, option)<<FRACBITS;
2579 	V_DrawStringAtFixed(x, y, option, string);
2580 }
2581 
2582 // Draws a small string at a fixed_t location.
V_DrawSmallStringAtFixed(fixed_t x,fixed_t y,INT32 option,const char * string)2583 void V_DrawSmallStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
2584 {
2585 	fixed_t cx = x, cy = y;
2586 	INT32 w, c, dupx, dupy, scrwidth, center = 0, left = 0;
2587 	const char *ch = string;
2588 	INT32 charflags = 0;
2589 	const UINT8 *colormap = NULL;
2590 	INT32 spacewidth = 2, charwidth = 0;
2591 
2592 	INT32 lowercase = (option & V_ALLOWLOWERCASE);
2593 	option &= ~V_FLIP; // which is also shared with V_ALLOWLOWERCASE...
2594 
2595 	if (option & V_NOSCALESTART)
2596 	{
2597 		dupx = vid.dupx;
2598 		dupy = vid.dupy;
2599 		scrwidth = vid.width;
2600 	}
2601 	else
2602 	{
2603 		dupx = dupy = 1;
2604 		scrwidth = vid.width/vid.dupx;
2605 		left = (scrwidth - BASEVIDWIDTH)/2;
2606 		scrwidth -= left;
2607 	}
2608 
2609 	if (option & V_NOSCALEPATCH)
2610 		scrwidth *= vid.dupx;
2611 
2612 	charflags = (option & V_CHARCOLORMASK);
2613 
2614 	switch (option & V_SPACINGMASK)
2615 	{
2616 		case V_MONOSPACE:
2617 			spacewidth = 4;
2618 			/* FALLTHRU */
2619 		case V_OLDSPACING:
2620 			charwidth = 4;
2621 			break;
2622 		case V_6WIDTHSPACE:
2623 			spacewidth = 3;
2624 		default:
2625 			break;
2626 	}
2627 
2628 	for (;;ch++)
2629 	{
2630 		if (!*ch)
2631 			break;
2632 		if (*ch & 0x80) //color parsing -x 2.16.09
2633 		{
2634 			// manually set flags override color codes
2635 			if (!(option & V_CHARCOLORMASK))
2636 				charflags = ((*ch & 0x7f) << V_CHARCOLORSHIFT) & V_CHARCOLORMASK;
2637 			continue;
2638 		}
2639 		if (*ch == '\n')
2640 		{
2641 			cx = x;
2642 
2643 			if (option & V_RETURN8)
2644 				cy += (4*dupy)<<FRACBITS;
2645 			else
2646 				cy += (6*dupy)<<FRACBITS;
2647 
2648 			continue;
2649 		}
2650 
2651 		c = *ch;
2652 		if (!lowercase)
2653 			c = toupper(c);
2654 		c -= HU_FONTSTART;
2655 
2656 		// character does not exist or is a space
2657 		if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
2658 		{
2659 			cx += (spacewidth * dupx)<<FRACBITS;
2660 			continue;
2661 		}
2662 
2663 		if (charwidth)
2664 		{
2665 			w = charwidth * dupx;
2666 			center = w/2 - hu_font[c]->width*(dupx/4);
2667 		}
2668 		else
2669 			w = hu_font[c]->width * dupx / 2;
2670 
2671 		if ((cx>>FRACBITS) > scrwidth)
2672 			break;
2673 		if ((cx>>FRACBITS)+left + w < 0) //left boundary check
2674 		{
2675 			cx += w<<FRACBITS;
2676 			continue;
2677 		}
2678 
2679 		colormap = V_GetStringColormap(charflags);
2680 
2681 		V_DrawFixedPatch(cx + (center<<FRACBITS), cy, FRACUNIT/2, option, hu_font[c], colormap);
2682 
2683 		cx += w<<FRACBITS;
2684 	}
2685 }
2686 
V_DrawCenteredSmallStringAtFixed(fixed_t x,fixed_t y,INT32 option,const char * string)2687 void V_DrawCenteredSmallStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
2688 {
2689 	x -= (V_SmallStringWidth(string, option) / 2)<<FRACBITS;
2690 	V_DrawSmallStringAtFixed(x, y, option, string);
2691 }
2692 
V_DrawRightAlignedSmallStringAtFixed(fixed_t x,fixed_t y,INT32 option,const char * string)2693 void V_DrawRightAlignedSmallStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
2694 {
2695 	x -= V_SmallStringWidth(string, option)<<FRACBITS;
2696 	V_DrawSmallStringAtFixed(x, y, option, string);
2697 }
2698 
2699 // Draws a thin string at a fixed_t location.
V_DrawThinStringAtFixed(fixed_t x,fixed_t y,INT32 option,const char * string)2700 void V_DrawThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
2701 {
2702 	fixed_t cx = x, cy = y;
2703 	INT32 w, c, dupx, dupy, scrwidth, center = 0, left = 0;
2704 	const char *ch = string;
2705 	INT32 charflags = 0;
2706 	const UINT8 *colormap = NULL;
2707 	INT32 spacewidth = 2, charwidth = 0;
2708 
2709 	INT32 lowercase = (option & V_ALLOWLOWERCASE);
2710 	option &= ~V_FLIP; // which is also shared with V_ALLOWLOWERCASE...
2711 
2712 	if (option & V_NOSCALESTART)
2713 	{
2714 		dupx = vid.dupx;
2715 		dupy = vid.dupy;
2716 		scrwidth = vid.width;
2717 	}
2718 	else
2719 	{
2720 		dupx = dupy = 1;
2721 		scrwidth = vid.width/vid.dupx;
2722 		left = (scrwidth - BASEVIDWIDTH)/2;
2723 		scrwidth -= left;
2724 	}
2725 
2726 	if (option & V_NOSCALEPATCH)
2727 		scrwidth *= vid.dupx;
2728 
2729 	charflags = (option & V_CHARCOLORMASK);
2730 
2731 	switch (option & V_SPACINGMASK)
2732 	{
2733 		case V_MONOSPACE:
2734 			spacewidth = 8;
2735 			/* FALLTHRU */
2736 		case V_OLDSPACING:
2737 			charwidth = 8;
2738 			break;
2739 		case V_6WIDTHSPACE:
2740 			spacewidth = 6;
2741 		default:
2742 			break;
2743 	}
2744 
2745 	for (;;ch++)
2746 	{
2747 		if (!*ch)
2748 			break;
2749 		if (*ch & 0x80) //color parsing -x 2.16.09
2750 		{
2751 			// manually set flags override color codes
2752 			if (!(option & V_CHARCOLORMASK))
2753 				charflags = ((*ch & 0x7f) << V_CHARCOLORSHIFT) & V_CHARCOLORMASK;
2754 			continue;
2755 		}
2756 		if (*ch == '\n')
2757 		{
2758 			cx = x;
2759 
2760 			if (option & V_RETURN8)
2761 				cy += (8*dupy)<<FRACBITS;
2762 			else
2763 				cy += (12*dupy)<<FRACBITS;
2764 
2765 			continue;
2766 		}
2767 
2768 		c = *ch;
2769 		if (!lowercase || !tny_font[c-HU_FONTSTART])
2770 			c = toupper(c);
2771 		c -= HU_FONTSTART;
2772 
2773 		// character does not exist or is a space
2774 		if (c < 0 || c >= HU_FONTSIZE || !tny_font[c])
2775 		{
2776 			cx += (spacewidth * dupx)<<FRACBITS;
2777 			continue;
2778 		}
2779 
2780 		if (charwidth)
2781 		{
2782 			w = charwidth * dupx;
2783 			center = w/2 - tny_font[c]->width*(dupx/2);
2784 		}
2785 		else
2786 			w = tny_font[c]->width * dupx;
2787 
2788 		if ((cx>>FRACBITS) > scrwidth)
2789 			break;
2790 		if ((cx>>FRACBITS)+left + w < 0) //left boundary check
2791 		{
2792 			cx += w<<FRACBITS;
2793 			continue;
2794 		}
2795 
2796 		colormap = V_GetStringColormap(charflags);
2797 
2798 		V_DrawFixedPatch(cx + (center<<FRACBITS), cy, FRACUNIT, option, tny_font[c], colormap);
2799 
2800 		cx += w<<FRACBITS;
2801 	}
2802 }
2803 
V_DrawCenteredThinStringAtFixed(fixed_t x,fixed_t y,INT32 option,const char * string)2804 void V_DrawCenteredThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
2805 {
2806 	x -= (V_ThinStringWidth(string, option) / 2)<<FRACBITS;
2807 	V_DrawThinStringAtFixed(x, y, option, string);
2808 }
2809 
V_DrawRightAlignedThinStringAtFixed(fixed_t x,fixed_t y,INT32 option,const char * string)2810 void V_DrawRightAlignedThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
2811 {
2812 	x -= V_ThinStringWidth(string, option)<<FRACBITS;
2813 	V_DrawThinStringAtFixed(x, y, option, string);
2814 }
2815 
2816 // Draws a small string at a fixed_t location.
V_DrawSmallThinStringAtFixed(fixed_t x,fixed_t y,INT32 option,const char * string)2817 void V_DrawSmallThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
2818 {
2819 	fixed_t cx = x, cy = y;
2820 	INT32 w, c, dupx, dupy, scrwidth, center = 0, left = 0;
2821 	const char *ch = string;
2822 	INT32 charflags = 0;
2823 	const UINT8 *colormap = NULL;
2824 	INT32 spacewidth = 2<<FRACBITS, charwidth = 0;
2825 
2826 	INT32 lowercase = (option & V_ALLOWLOWERCASE);
2827 	option &= ~V_FLIP; // which is also shared with V_ALLOWLOWERCASE...
2828 
2829 	if (option & V_NOSCALESTART)
2830 	{
2831 		dupx = vid.dupx<<FRACBITS;
2832 		dupy = vid.dupy<<FRACBITS;
2833 		scrwidth = vid.width;
2834 	}
2835 	else
2836 	{
2837 		dupx = dupy = FRACUNIT;
2838 		scrwidth = FixedDiv(vid.width<<FRACBITS, vid.dupx);
2839 		left = ((scrwidth - (BASEVIDWIDTH<<FRACBITS))/2);
2840 		scrwidth -= left;
2841 	}
2842 
2843 	if (option & V_NOSCALEPATCH)
2844 		scrwidth *= vid.dupx;
2845 
2846 	charflags = (option & V_CHARCOLORMASK);
2847 
2848 	switch (option & V_SPACINGMASK)
2849 	{
2850 		case V_MONOSPACE:
2851 			spacewidth = 4<<FRACBITS;
2852 			/* FALLTHRU */
2853 		case V_OLDSPACING:
2854 			charwidth = 4<<FRACBITS;
2855 			break;
2856 		case V_6WIDTHSPACE:
2857 			spacewidth = 3<<FRACBITS;
2858 		default:
2859 			break;
2860 	}
2861 
2862 	for (;;ch++)
2863 	{
2864 		if (!*ch)
2865 			break;
2866 		if (*ch & 0x80) //color parsing -x 2.16.09
2867 		{
2868 			// manually set flags override color codes
2869 			if (!(option & V_CHARCOLORMASK))
2870 				charflags = ((*ch & 0x7f) << V_CHARCOLORSHIFT) & V_CHARCOLORMASK;
2871 			continue;
2872 		}
2873 		if (*ch == '\n')
2874 		{
2875 			cx = x;
2876 
2877 			if (option & V_RETURN8)
2878 				cy += 4*dupy;
2879 			else
2880 				cy += 6*dupy;
2881 
2882 			continue;
2883 		}
2884 
2885 		c = *ch;
2886 		if (!lowercase)
2887 			c = toupper(c);
2888 		c -= HU_FONTSTART;
2889 
2890 		// character does not exist or is a space
2891 		if (c < 0 || c >= HU_FONTSIZE || !tny_font[c])
2892 		{
2893 			cx += FixedMul(spacewidth, dupx);
2894 			continue;
2895 		}
2896 
2897 		if (charwidth)
2898 		{
2899 			w = FixedMul(charwidth, dupx);
2900 			center = w/2 - tny_font[c]->width*(dupx/4);
2901 		}
2902 		else
2903 			w = tny_font[c]->width * dupx / 2;
2904 
2905 		if (cx > scrwidth)
2906 			break;
2907 		if (cx+left + w < 0) //left boundary check
2908 		{
2909 			cx += w;
2910 			continue;
2911 		}
2912 
2913 		colormap = V_GetStringColormap(charflags);
2914 
2915 		V_DrawFixedPatch(cx + center, cy, FRACUNIT/2, option, tny_font[c], colormap);
2916 
2917 		cx += w;
2918 	}
2919 }
2920 
V_DrawCenteredSmallThinStringAtFixed(fixed_t x,fixed_t y,INT32 option,const char * string)2921 void V_DrawCenteredSmallThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
2922 {
2923 	x -= V_SmallThinStringWidth(string, option)/4;
2924 	V_DrawSmallThinStringAtFixed(x, y, option, string);
2925 }
2926 
V_DrawRightAlignedSmallThinStringAtFixed(fixed_t x,fixed_t y,INT32 option,const char * string)2927 void V_DrawRightAlignedSmallThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
2928 {
2929 	x -= V_SmallThinStringWidth(string, option)/2;
2930 	V_DrawSmallThinStringAtFixed(x, y, option, string);
2931 }
2932 
2933 // Draws a tallnum.  Replaces two functions in y_inter and st_stuff
V_DrawTallNum(INT32 x,INT32 y,INT32 flags,INT32 num)2934 void V_DrawTallNum(INT32 x, INT32 y, INT32 flags, INT32 num)
2935 {
2936 	INT32 w = tallnum[0]->width;
2937 	boolean neg;
2938 
2939 	if (flags & (V_NOSCALESTART|V_NOSCALEPATCH))
2940 		w *= vid.dupx;
2941 
2942 	if ((neg = num < 0))
2943 		num = -num;
2944 
2945 	// draw the number
2946 	do
2947 	{
2948 		x -= w;
2949 		V_DrawScaledPatch(x, y, flags, tallnum[num % 10]);
2950 		num /= 10;
2951 	} while (num);
2952 
2953 	// draw a minus sign if necessary
2954 	if (neg)
2955 		V_DrawScaledPatch(x - w, y, flags, tallminus); // Tails
2956 }
2957 
2958 // Draws a number with a set number of digits.
2959 // Does not handle negative numbers in a special way, don't try to feed it any.
V_DrawPaddedTallNum(INT32 x,INT32 y,INT32 flags,INT32 num,INT32 digits)2960 void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits)
2961 {
2962 	INT32 w = tallnum[0]->width;
2963 
2964 	if (flags & (V_NOSCALESTART|V_NOSCALEPATCH))
2965 		w *= vid.dupx;
2966 
2967 	if (num < 0)
2968 		num = -num;
2969 
2970 	// draw the number
2971 	do
2972 	{
2973 		x -= w;
2974 		V_DrawScaledPatch(x, y, flags, tallnum[num % 10]);
2975 		num /= 10;
2976 	} while (--digits);
2977 }
2978 
2979 // Draw an act number for a level title
V_DrawLevelActNum(INT32 x,INT32 y,INT32 flags,UINT8 num)2980 void V_DrawLevelActNum(INT32 x, INT32 y, INT32 flags, UINT8 num)
2981 {
2982 	if (num > 99)
2983 		return; // not supported
2984 
2985 	while (num > 0)
2986 	{
2987 		if (num > 9) // if there are two digits, draw second digit first
2988 			V_DrawScaledPatch(x + (V_LevelActNumWidth(num) - V_LevelActNumWidth(num%10)), y, flags, ttlnum[num%10]);
2989 		else
2990 			V_DrawScaledPatch(x, y, flags, ttlnum[num]);
2991 		num = num/10;
2992 	}
2993 }
2994 
2995 // Write a string using the credit font
2996 // NOTE: the text is centered for screens larger than the base width
2997 //
V_DrawCreditString(fixed_t x,fixed_t y,INT32 option,const char * string)2998 void V_DrawCreditString(fixed_t x, fixed_t y, INT32 option, const char *string)
2999 {
3000 	INT32 w, c, dupx, dupy, scrwidth = BASEVIDWIDTH;
3001 	fixed_t cx = x, cy = y;
3002 	const char *ch = string;
3003 
3004 	// It's possible for string to be a null pointer
3005 	if (!string)
3006 		return;
3007 
3008 	if (option & V_NOSCALESTART)
3009 	{
3010 		dupx = vid.dupx;
3011 		dupy = vid.dupy;
3012 		scrwidth = vid.width;
3013 	}
3014 	else
3015 		dupx = dupy = 1;
3016 
3017 	if (option & V_NOSCALEPATCH)
3018 		scrwidth *= vid.dupx;
3019 
3020 	for (;;)
3021 	{
3022 		c = *ch++;
3023 		if (!c)
3024 			break;
3025 		if (c == '\n')
3026 		{
3027 			cx = x;
3028 			cy += (12*dupy)<<FRACBITS;
3029 			continue;
3030 		}
3031 
3032 		c = toupper(c) - CRED_FONTSTART;
3033 		if (c < 0 || c >= CRED_FONTSIZE)
3034 		{
3035 			cx += (16*dupx)<<FRACBITS;
3036 			continue;
3037 		}
3038 
3039 		w = cred_font[c]->width * dupx;
3040 		if ((cx>>FRACBITS) > scrwidth)
3041 			continue;
3042 
3043 		V_DrawSciencePatch(cx, cy, option, cred_font[c], FRACUNIT);
3044 		cx += w<<FRACBITS;
3045 	}
3046 }
3047 
3048 // Draw a string using the nt_font
3049 // Note that the outline is a seperate font set
V_DrawNameTagLine(INT32 x,INT32 y,INT32 option,fixed_t scale,UINT8 * basecolormap,UINT8 * outlinecolormap,const char * string)3050 static void V_DrawNameTagLine(INT32 x, INT32 y, INT32 option, fixed_t scale, UINT8 *basecolormap, UINT8 *outlinecolormap, const char *string)
3051 {
3052 	fixed_t cx, cy, w;
3053 	INT32 c, dupx, dupy, scrwidth, left = 0;
3054 	const char *ch = string;
3055 
3056 	if (option & V_CENTERNAMETAG)
3057 		x -= FixedInt(FixedMul((V_NameTagWidth(string)/2)*FRACUNIT, scale));
3058 	option &= ~V_CENTERNAMETAG; // which is also shared with V_ALLOWLOWERCASE...
3059 
3060 	cx = x<<FRACBITS;
3061 	cy = y<<FRACBITS;
3062 
3063 	if (option & V_NOSCALESTART)
3064 	{
3065 		dupx = vid.dupx;
3066 		dupy = vid.dupy;
3067 		scrwidth = vid.width;
3068 	}
3069 	else
3070 	{
3071 		dupx = dupy = 1;
3072 		scrwidth = vid.width/vid.dupx;
3073 		left = (scrwidth - BASEVIDWIDTH)/2;
3074 		scrwidth -= left;
3075 	}
3076 
3077 	if (option & V_NOSCALEPATCH)
3078 		scrwidth *= vid.dupx;
3079 
3080 	for (;;ch++)
3081 	{
3082 		if (!*ch)
3083 			break;
3084 		if (*ch == '\n')
3085 		{
3086 			cx = x<<FRACBITS;
3087 			cy += FixedMul((21*dupy)*FRACUNIT, scale);
3088 			continue;
3089 		}
3090 
3091 		c = toupper(*ch);
3092 		c -= NT_FONTSTART;
3093 
3094 		// character does not exist or is a space
3095 		if (c < 0 || c >= NT_FONTSIZE || !ntb_font[c] || !nto_font[c])
3096 		{
3097 			cx += FixedMul((4 * dupx)*FRACUNIT, scale);
3098 			continue;
3099 		}
3100 
3101 		w = FixedMul(((ntb_font[c]->width)+2 * dupx) * FRACUNIT, scale);
3102 
3103 		if (FixedInt(cx) > scrwidth)
3104 			continue;
3105 		if (cx+(left*FRACUNIT) + w < 0) // left boundary check
3106 		{
3107 			cx += w;
3108 			continue;
3109 		}
3110 
3111 		V_DrawFixedPatch(cx, cy, scale, option, nto_font[c], outlinecolormap);
3112 		V_DrawFixedPatch(cx, cy, scale, option, ntb_font[c], basecolormap);
3113 
3114 		cx += w;
3115 	}
3116 }
3117 
3118 // Looks familiar.
V_DrawNameTag(INT32 x,INT32 y,INT32 option,fixed_t scale,UINT8 * basecolormap,UINT8 * outlinecolormap,const char * string)3119 void V_DrawNameTag(INT32 x, INT32 y, INT32 option, fixed_t scale, UINT8 *basecolormap, UINT8 *outlinecolormap, const char *string)
3120 {
3121 	const char *text = string;
3122 	const char *first_token = text;
3123 	char *last_token = strchr(text, '\n');
3124 	const INT32 lbreakheight = 21;
3125 	INT32 ntlines;
3126 
3127 	if (option & V_CENTERNAMETAG)
3128 	{
3129 		ntlines = V_CountNameTagLines(string);
3130 		y -= FixedInt(FixedMul(((lbreakheight/2) * (ntlines-1))*FRACUNIT, scale));
3131 	}
3132 
3133 	// No line breaks?
3134 	// Draw entire string
3135 	if (!last_token)
3136 		V_DrawNameTagLine(x, y, option, scale, basecolormap, outlinecolormap, string);
3137 	// Split string by the line break character
3138 	else
3139 	{
3140 		char *str = NULL;
3141 		INT32 len;
3142 		while (true)
3143 		{
3144 			// There are still lines left to draw
3145 			if (last_token)
3146 			{
3147 				size_t shift = 0;
3148 				// Free this line
3149 				if (str)
3150 					Z_Free(str);
3151 				// Find string length, do a malloc...
3152 				len = (last_token-first_token)+1;
3153 				str = ZZ_Alloc(len);
3154 				// Copy the line
3155 				strncpy(str, first_token, len-1);
3156 				str[len-1] = '\0';
3157 				// Don't leave a line break character
3158 				// at the start of the string!
3159 				if ((strlen(str) >= 2) && (string[0] == '\n') && (string[1] != '\n'))
3160 					shift++;
3161 				// Then draw it
3162 				V_DrawNameTagLine(x, y, option, scale, basecolormap, outlinecolormap, str+shift);
3163 			}
3164 			// No line break character was found
3165 			else
3166 			{
3167 				// Don't leave a line break character
3168 				// at the start of the string!
3169 				if ((strlen(first_token) >= 2) && (first_token[0] == '\n') && (first_token[1] != '\n'))
3170 					first_token++;
3171 				// Then draw it
3172 				V_DrawNameTagLine(x, y, option, scale, basecolormap, outlinecolormap, first_token);
3173 				break;
3174 			}
3175 
3176 			// Next line
3177 			y += FixedInt(FixedMul(lbreakheight*FRACUNIT, scale));
3178 			if ((last_token-text)+1 >= (signed)strlen(text))
3179 				last_token = NULL;
3180 			else
3181 			{
3182 				first_token = last_token;
3183 				last_token = strchr(first_token+1, '\n');
3184 			}
3185 		}
3186 		// Free this line
3187 		if (str)
3188 			Z_Free(str);
3189 	}
3190 }
3191 
3192 // Count the amount of lines in name tag string
V_CountNameTagLines(const char * string)3193 INT32 V_CountNameTagLines(const char *string)
3194 {
3195 	INT32 ntlines = 1;
3196 	const char *text = string;
3197 	const char *first_token = text;
3198 	char *last_token = strchr(text, '\n');
3199 
3200 	// No line breaks?
3201 	if (!last_token)
3202 		return ntlines;
3203 	// Split string by the line break character
3204 	else
3205 	{
3206 		while (true)
3207 		{
3208 			if (last_token)
3209 				ntlines++;
3210 			// No line break character was found
3211 			else
3212 				break;
3213 
3214 			// Next line
3215 			if ((last_token-text)+1 >= (signed)strlen(text))
3216 				last_token = NULL;
3217 			else
3218 			{
3219 				first_token = last_token;
3220 				last_token = strchr(first_token+1, '\n');
3221 			}
3222 		}
3223 	}
3224 	return ntlines;
3225 }
3226 
V_NameTagWidth(const char * string)3227 INT32 V_NameTagWidth(const char *string)
3228 {
3229 	INT32 c, w = 0;
3230 	size_t i;
3231 
3232 	// It's possible for string to be a null pointer
3233 	if (!string)
3234 		return 0;
3235 
3236 	for (i = 0; i < strlen(string); i++)
3237 	{
3238 		c = toupper(string[i]) - NT_FONTSTART;
3239 		if (c < 0 || c >= NT_FONTSIZE || !ntb_font[c] || !nto_font[c])
3240 			w += 4;
3241 		else
3242 			w += (ntb_font[c]->width)+2;
3243 	}
3244 
3245 	return w;
3246 }
3247 
3248 // Find string width from cred_font chars
3249 //
V_CreditStringWidth(const char * string)3250 INT32 V_CreditStringWidth(const char *string)
3251 {
3252 	INT32 c, w = 0;
3253 	size_t i;
3254 
3255 	// It's possible for string to be a null pointer
3256 	if (!string)
3257 		return 0;
3258 
3259 	for (i = 0; i < strlen(string); i++)
3260 	{
3261 		c = toupper(string[i]) - CRED_FONTSTART;
3262 		if (c < 0 || c >= CRED_FONTSIZE)
3263 			w += 16;
3264 		else
3265 			w += cred_font[c]->width;
3266 	}
3267 
3268 	return w;
3269 }
3270 
3271 // Write a string using the level title font
3272 // NOTE: the text is centered for screens larger than the base width
3273 //
V_DrawLevelTitle(INT32 x,INT32 y,INT32 option,const char * string)3274 void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string)
3275 {
3276 	INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, left = 0;
3277 	const char *ch = string;
3278 	INT32 charflags = (option & V_CHARCOLORMASK);
3279 	const UINT8 *colormap = NULL;
3280 
3281 	if (option & V_NOSCALESTART)
3282 	{
3283 		dupx = vid.dupx;
3284 		dupy = vid.dupy;
3285 		scrwidth = vid.width;
3286 	}
3287 	else
3288 	{
3289 		dupx = dupy = 1;
3290 		scrwidth = vid.width/vid.dupx;
3291 		left = (scrwidth - BASEVIDWIDTH)/2;
3292 		scrwidth -= left;
3293 	}
3294 
3295 	if (option & V_NOSCALEPATCH)
3296 		scrwidth *= vid.dupx;
3297 
3298 	for (;;ch++)
3299 	{
3300 		if (!*ch)
3301 			break;
3302 		if (*ch & 0x80) //color parsing -x 2.16.09
3303 		{
3304 			// manually set flags override color codes
3305 			if (!(option & V_CHARCOLORMASK))
3306 				charflags = ((*ch & 0x7f) << V_CHARCOLORSHIFT) & V_CHARCOLORMASK;
3307 			continue;
3308 		}
3309 		if (*ch == '\n')
3310 		{
3311 			cx = x;
3312 			cy += 12*dupy;
3313 			continue;
3314 		}
3315 
3316 		c = *ch - LT_FONTSTART;
3317 		if (c < 0 || c >= LT_FONTSIZE || !lt_font[c])
3318 		{
3319 			cx += 16*dupx;
3320 			continue;
3321 		}
3322 
3323 		w = lt_font[c]->width * dupx;
3324 
3325 		if (cx > scrwidth)
3326 			continue;
3327 		if (cx+left + w < 0) //left boundary check
3328 		{
3329 			cx += w;
3330 			continue;
3331 		}
3332 
3333 		colormap = V_GetStringColormap(charflags);
3334 		V_DrawFixedPatch(cx<<FRACBITS, cy<<FRACBITS, FRACUNIT, option, lt_font[c], colormap);
3335 
3336 		cx += w;
3337 	}
3338 }
3339 
3340 // Find string width from lt_font chars
3341 //
V_LevelNameWidth(const char * string)3342 INT32 V_LevelNameWidth(const char *string)
3343 {
3344 	INT32 c, w = 0;
3345 	size_t i;
3346 
3347 	for (i = 0; i < strlen(string); i++)
3348 	{
3349 		if (string[i] & 0x80)
3350 			continue;
3351 		c = string[i] - LT_FONTSTART;
3352 		if (c < 0 || c >= LT_FONTSIZE || !lt_font[c])
3353 			w += 16;
3354 		else
3355 			w += lt_font[c]->width;
3356 	}
3357 
3358 	return w;
3359 }
3360 
3361 // Find max height of the string
3362 //
V_LevelNameHeight(const char * string)3363 INT32 V_LevelNameHeight(const char *string)
3364 {
3365 	INT32 c, w = 0;
3366 	size_t i;
3367 
3368 	for (i = 0; i < strlen(string); i++)
3369 	{
3370 		c = string[i] - LT_FONTSTART;
3371 		if (c < 0 || c >= LT_FONTSIZE || !lt_font[c])
3372 			continue;
3373 
3374 		if (lt_font[c]->height > w)
3375 			w = lt_font[c]->height;
3376 	}
3377 
3378 	return w;
3379 }
3380 
3381 // For ST_drawTitleCard
3382 // Returns the width of the act num patch(es)
V_LevelActNumWidth(UINT8 num)3383 INT16 V_LevelActNumWidth(UINT8 num)
3384 {
3385 	INT16 result = 0;
3386 
3387 	if (num == 0)
3388 		result = ttlnum[num]->width;
3389 
3390 	while (num > 0 && num <= 99)
3391 	{
3392 		result = result + ttlnum[num%10]->width;
3393 		num = num/10;
3394 	}
3395 
3396 	return result;
3397 }
3398 
3399 //
3400 // Find string width from hu_font chars
3401 //
V_StringWidth(const char * string,INT32 option)3402 INT32 V_StringWidth(const char *string, INT32 option)
3403 {
3404 	INT32 c, w = 0;
3405 	INT32 spacewidth = 4, charwidth = 0;
3406 	size_t i;
3407 
3408 	switch (option & V_SPACINGMASK)
3409 	{
3410 		case V_MONOSPACE:
3411 			spacewidth = 8;
3412 			/* FALLTHRU */
3413 		case V_OLDSPACING:
3414 			charwidth = 8;
3415 			break;
3416 		case V_6WIDTHSPACE:
3417 			spacewidth = 6;
3418 		default:
3419 			break;
3420 	}
3421 
3422 	for (i = 0; i < strlen(string); i++)
3423 	{
3424 		if (string[i] & 0x80)
3425 			continue;
3426 		c = toupper(string[i]) - HU_FONTSTART;
3427 		if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
3428 			w += spacewidth;
3429 		else
3430 			w += (charwidth ? charwidth : hu_font[c]->width);
3431 	}
3432 
3433 	if (option & (V_NOSCALESTART|V_NOSCALEPATCH))
3434 		w *= vid.dupx;
3435 
3436 	return w;
3437 }
3438 
3439 //
3440 // Find string width from hu_font chars, 0.5x scale
3441 //
V_SmallStringWidth(const char * string,INT32 option)3442 INT32 V_SmallStringWidth(const char *string, INT32 option)
3443 {
3444 	INT32 c, w = 0;
3445 	INT32 spacewidth = 2, charwidth = 0;
3446 	size_t i;
3447 
3448 	switch (option & V_SPACINGMASK)
3449 	{
3450 		case V_MONOSPACE:
3451 			spacewidth = 4;
3452 			/* FALLTHRU */
3453 		case V_OLDSPACING:
3454 			charwidth = 4;
3455 			break;
3456 		case V_6WIDTHSPACE:
3457 			spacewidth = 3;
3458 		default:
3459 			break;
3460 	}
3461 
3462 	for (i = 0; i < strlen(string); i++)
3463 	{
3464 		if (string[i] & 0x80)
3465 			continue;
3466 		c = toupper(string[i]) - HU_FONTSTART;
3467 		if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
3468 			w += spacewidth;
3469 		else
3470 			w += (charwidth ? charwidth : (hu_font[c]->width / 2));
3471 	}
3472 
3473 	return w;
3474 }
3475 
3476 //
3477 // Find string width from tny_font chars
3478 //
V_ThinStringWidth(const char * string,INT32 option)3479 INT32 V_ThinStringWidth(const char *string, INT32 option)
3480 {
3481 	INT32 c, w = 0;
3482 	INT32 spacewidth = 2, charwidth = 0;
3483 	size_t i;
3484 
3485 	switch (option & V_SPACINGMASK)
3486 	{
3487 		case V_MONOSPACE:
3488 			spacewidth = 5;
3489 			/* FALLTHRU */
3490 		case V_OLDSPACING:
3491 			charwidth = 5;
3492 			break;
3493 		case V_6WIDTHSPACE:
3494 			spacewidth = 3;
3495 		default:
3496 			break;
3497 	}
3498 
3499 	for (i = 0; i < strlen(string); i++)
3500 	{
3501 		if (string[i] & 0x80)
3502 			continue;
3503 		c = toupper(string[i]) - HU_FONTSTART;
3504 		if (c < 0 || c >= HU_FONTSIZE || !tny_font[c])
3505 			w += spacewidth;
3506 		else
3507 			w += (charwidth ? charwidth : tny_font[c]->width);
3508 	}
3509 
3510 	return w;
3511 }
3512 
3513 //
3514 // Find string width from tny_font chars, 0.5x scale
3515 //
V_SmallThinStringWidth(const char * string,INT32 option)3516 INT32 V_SmallThinStringWidth(const char *string, INT32 option)
3517 {
3518 	INT32 w = V_ThinStringWidth(string, option)<<FRACBITS;
3519 	return w/2 + FRACUNIT; // +FRACUNIT because otherwise it's offset wrong.
3520 }
3521 
3522 boolean *heatshifter = NULL;
3523 INT32 lastheight = 0;
3524 INT32 heatindex[2] = { 0, 0 };
3525 
3526 //
3527 // V_DoPostProcessor
3528 //
3529 // Perform a particular image postprocessing function.
3530 //
3531 #include "p_local.h"
V_DoPostProcessor(INT32 view,postimg_t type,INT32 param)3532 void V_DoPostProcessor(INT32 view, postimg_t type, INT32 param)
3533 {
3534 #if NUMSCREENS < 5
3535 	// do not enable image post processing for ARM, SH and MIPS CPUs
3536 	(void)view;
3537 	(void)type;
3538 	(void)param;
3539 #else
3540 	INT32 height, yoffset;
3541 
3542 #ifdef HWRENDER
3543 	if (rendermode != render_soft)
3544 		return;
3545 #endif
3546 
3547 	if (view < 0 || view >= 2 || (view == 1 && !splitscreen))
3548 		return;
3549 
3550 	if (splitscreen)
3551 		height = vid.height/2;
3552 	else
3553 		height = vid.height;
3554 
3555 	if (view == 1)
3556 		yoffset = vid.height/2;
3557 	else
3558 		yoffset = 0;
3559 
3560 	if (type == postimg_water)
3561 	{
3562 		UINT8 *tmpscr = screens[4];
3563 		UINT8 *srcscr = screens[0];
3564 		INT32 y;
3565 		angle_t disStart = (leveltime * 128) & FINEMASK; // in 0 to FINEANGLE
3566 		INT32 newpix;
3567 		INT32 sine;
3568 		//UINT8 *transme = R_GetTranslucencyTable(tr_trans50);
3569 
3570 		for (y = yoffset; y < yoffset+height; y++)
3571 		{
3572 			sine = (FINESINE(disStart)*5)>>FRACBITS;
3573 			newpix = abs(sine);
3574 
3575 			if (sine < 0)
3576 			{
3577 				M_Memcpy(&tmpscr[y*vid.width+newpix], &srcscr[y*vid.width], vid.width-newpix);
3578 
3579 				// Cleanup edge
3580 				while (newpix)
3581 				{
3582 					tmpscr[y*vid.width+newpix] = srcscr[y*vid.width];
3583 					newpix--;
3584 				}
3585 			}
3586 			else
3587 			{
3588 				M_Memcpy(&tmpscr[y*vid.width+0], &srcscr[y*vid.width+sine], vid.width-newpix);
3589 
3590 				// Cleanup edge
3591 				while (newpix)
3592 				{
3593 					tmpscr[y*vid.width+vid.width-newpix] = srcscr[y*vid.width+(vid.width-1)];
3594 					newpix--;
3595 				}
3596 			}
3597 
3598 /*
3599 Unoptimized version
3600 			for (x = 0; x < vid.width*vid.bpp; x++)
3601 			{
3602 				newpix = (x + sine);
3603 
3604 				if (newpix < 0)
3605 					newpix = 0;
3606 				else if (newpix >= vid.width)
3607 					newpix = vid.width-1;
3608 
3609 				tmpscr[y*vid.width + x] = srcscr[y*vid.width+newpix]; // *(transme + (srcscr[y*vid.width+x]<<8) + srcscr[y*vid.width+newpix]);
3610 			}*/
3611 			disStart += 22;//the offset into the displacement map, increment each game loop
3612 			disStart &= FINEMASK; //clip it to FINEMASK
3613 		}
3614 
3615 		VID_BlitLinearScreen(tmpscr+vid.width*vid.bpp*yoffset, screens[0]+vid.width*vid.bpp*yoffset,
3616 				vid.width*vid.bpp, height, vid.width*vid.bpp, vid.width);
3617 	}
3618 	else if (type == postimg_motion) // Motion Blur!
3619 	{
3620 		UINT8 *tmpscr = screens[4];
3621 		UINT8 *srcscr = screens[0];
3622 		INT32 x, y;
3623 
3624 		// TODO: Add a postimg_param so that we can pick the translucency level...
3625 		UINT8 *transme = R_GetTranslucencyTable(param);
3626 
3627 		for (y = yoffset; y < yoffset+height; y++)
3628 		{
3629 			for (x = 0; x < vid.width; x++)
3630 			{
3631 				tmpscr[y*vid.width + x]
3632 					=     colormaps[*(transme     + (srcscr   [y*vid.width+x ] <<8) + (tmpscr[y*vid.width+x]))];
3633 			}
3634 		}
3635 		VID_BlitLinearScreen(tmpscr+vid.width*vid.bpp*yoffset, screens[0]+vid.width*vid.bpp*yoffset,
3636 				vid.width*vid.bpp, height, vid.width*vid.bpp, vid.width);
3637 	}
3638 	else if (type == postimg_flip) // Flip the screen upside-down
3639 	{
3640 		UINT8 *tmpscr = screens[4];
3641 		UINT8 *srcscr = screens[0];
3642 		INT32 y, y2;
3643 
3644 		for (y = yoffset, y2 = yoffset+height - 1; y < yoffset+height; y++, y2--)
3645 			M_Memcpy(&tmpscr[y2*vid.width], &srcscr[y*vid.width], vid.width);
3646 
3647 		VID_BlitLinearScreen(tmpscr+vid.width*vid.bpp*yoffset, screens[0]+vid.width*vid.bpp*yoffset,
3648 				vid.width*vid.bpp, height, vid.width*vid.bpp, vid.width);
3649 	}
3650 	else if (type == postimg_heat) // Heat wave
3651 	{
3652 		UINT8 *tmpscr = screens[4];
3653 		UINT8 *srcscr = screens[0];
3654 		INT32 y;
3655 
3656 		// Make sure table is built
3657 		if (heatshifter == NULL || lastheight != height)
3658 		{
3659 			if (heatshifter)
3660 				Z_Free(heatshifter);
3661 
3662 			heatshifter = Z_Calloc(height * sizeof(boolean), PU_STATIC, NULL);
3663 
3664 			for (y = 0; y < height; y++)
3665 			{
3666 				if (M_RandomChance(FRACUNIT/8)) // 12.5%
3667 					heatshifter[y] = true;
3668 			}
3669 
3670 			heatindex[0] = heatindex[1] = 0;
3671 			lastheight = height;
3672 		}
3673 
3674 		for (y = yoffset; y < yoffset+height; y++)
3675 		{
3676 			if (heatshifter[heatindex[view]++])
3677 			{
3678 				// Shift this row of pixels to the right by 2
3679 				tmpscr[y*vid.width] = srcscr[y*vid.width];
3680 				M_Memcpy(&tmpscr[y*vid.width+vid.dupx], &srcscr[y*vid.width], vid.width-vid.dupx);
3681 			}
3682 			else
3683 				M_Memcpy(&tmpscr[y*vid.width], &srcscr[y*vid.width], vid.width);
3684 
3685 			heatindex[view] %= height;
3686 		}
3687 
3688 		heatindex[view]++;
3689 		heatindex[view] %= vid.height;
3690 
3691 		VID_BlitLinearScreen(tmpscr+vid.width*vid.bpp*yoffset, screens[0]+vid.width*vid.bpp*yoffset,
3692 				vid.width*vid.bpp, height, vid.width*vid.bpp, vid.width);
3693 	}
3694 #endif
3695 }
3696 
3697 // Generates a RGB565 color look-up table
InitColorLUT(colorlookup_t * lut,RGBA_t * palette,boolean makecolors)3698 void InitColorLUT(colorlookup_t *lut, RGBA_t *palette, boolean makecolors)
3699 {
3700 	size_t palsize = (sizeof(RGBA_t) * 256);
3701 
3702 	if (!lut->init || memcmp(lut->palette, palette, palsize))
3703 	{
3704 		INT32 i;
3705 
3706 		lut->init = true;
3707 		memcpy(lut->palette, palette, palsize);
3708 
3709 		for (i = 0; i < 0xFFFF; i++)
3710 			lut->table[i] = 0xFFFF;
3711 
3712 		if (makecolors)
3713 		{
3714 			UINT8 r, g, b;
3715 
3716 			for (r = 0; r < 0xFF; r++)
3717 			for (g = 0; g < 0xFF; g++)
3718 			for (b = 0; b < 0xFF; b++)
3719 			{
3720 				i = CLUTINDEX(r, g, b);
3721 				if (lut->table[i] == 0xFFFF)
3722 					lut->table[i] = NearestPaletteColor(r, g, b, palette);
3723 			}
3724 		}
3725 	}
3726 }
3727 
GetColorLUT(colorlookup_t * lut,UINT8 r,UINT8 g,UINT8 b)3728 UINT8 GetColorLUT(colorlookup_t *lut, UINT8 r, UINT8 g, UINT8 b)
3729 {
3730 	INT32 i = CLUTINDEX(r, g, b);
3731 	if (lut->table[i] == 0xFFFF)
3732 		lut->table[i] = NearestPaletteColor(r, g, b, lut->palette);
3733 	return lut->table[i];
3734 }
3735 
GetColorLUTDirect(colorlookup_t * lut,UINT8 r,UINT8 g,UINT8 b)3736 UINT8 GetColorLUTDirect(colorlookup_t *lut, UINT8 r, UINT8 g, UINT8 b)
3737 {
3738 	INT32 i = CLUTINDEX(r, g, b);
3739 	return lut->table[i];
3740 }
3741 
3742 // V_Init
3743 // old software stuff, buffers are allocated at video mode setup
3744 // here we set the screens[x] pointers accordingly
3745 // WARNING: called at runtime (don't init cvar here)
V_Init(void)3746 void V_Init(void)
3747 {
3748 	INT32 i;
3749 	UINT8 *base = vid.buffer;
3750 	const INT32 screensize = vid.rowbytes * vid.height;
3751 
3752 	LoadMapPalette();
3753 
3754 	for (i = 0; i < NUMSCREENS; i++)
3755 		screens[i] = NULL;
3756 
3757 	// start address of NUMSCREENS * width*height vidbuffers
3758 	if (base)
3759 	{
3760 		for (i = 0; i < NUMSCREENS; i++)
3761 			screens[i] = base + i*screensize;
3762 	}
3763 
3764 	if (vid.direct)
3765 		screens[0] = vid.direct;
3766 
3767 #ifdef DEBUG
3768 	CONS_Debug(DBG_RENDER, "V_Init done:\n");
3769 	for (i = 0; i < NUMSCREENS; i++)
3770 		CONS_Debug(DBG_RENDER, " screens[%d] = %x\n", i, screens[i]);
3771 #endif
3772 }
3773 
V_Recalc(void)3774 void V_Recalc(void)
3775 {
3776 	// scale 1,2,3 times in x and y the patches for the menus and overlays...
3777 	// calculated once and for all, used by routines in v_video.c and v_draw.c
3778 	vid.dupx = vid.width / BASEVIDWIDTH;
3779 	vid.dupy = vid.height / BASEVIDHEIGHT;
3780 	vid.dupx = vid.dupy = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy);
3781 	vid.fdupx = FixedDiv(vid.width*FRACUNIT, BASEVIDWIDTH*FRACUNIT);
3782 	vid.fdupy = FixedDiv(vid.height*FRACUNIT, BASEVIDHEIGHT*FRACUNIT);
3783 
3784 #ifdef HWRENDER
3785 	//if (rendermode != render_opengl && rendermode != render_none) // This was just placing it incorrectly at non aspect correct resolutions in opengl
3786 	// 13/11/18:
3787 	// The above is no longer necessary, since we want OpenGL to be just like software now
3788 	// -- Monster Iestyn
3789 #endif
3790 		vid.fdupx = vid.fdupy = (vid.fdupx < vid.fdupy ? vid.fdupx : vid.fdupy);
3791 
3792 	vid.meddupx = (UINT8)(vid.dupx >> 1) + 1;
3793 	vid.meddupy = (UINT8)(vid.dupy >> 1) + 1;
3794 #ifdef HWRENDER
3795 	vid.fmeddupx = vid.meddupx*FRACUNIT;
3796 	vid.fmeddupy = vid.meddupy*FRACUNIT;
3797 #endif
3798 
3799 	vid.smalldupx = (UINT8)(vid.dupx / 3) + 1;
3800 	vid.smalldupy = (UINT8)(vid.dupy / 3) + 1;
3801 #ifdef HWRENDER
3802 	vid.fsmalldupx = vid.smalldupx*FRACUNIT;
3803 	vid.fsmalldupy = vid.smalldupy*FRACUNIT;
3804 #endif
3805 }
3806