RingRacers/src/v_video.c
Sally Coolatta 631f698432 Cleanup
So for whatever reason it's still giving me a warning about functions in i_threads having duplicate definitions, even though they clearly do not.

I'm completely stumped so I'm just gonna mess with my compile setup....... in the mean time, if anyone else could try to compile it, I'd appreciate it, so we know if it's just my end or if its something else
2020-08-15 08:32:56 -04:00

2606 lines
64 KiB
C

// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file v_video.c
/// \brief Gamma correction LUT stuff
/// Functions to draw patches (by post) directly to screen.
/// Functions to blit a block to the screen.
#include "doomdef.h"
#include "r_local.h"
#include "p_local.h" // stplyr
#include "g_game.h" // players
#include "v_video.h"
#include "st_stuff.h"
#include "hu_stuff.h"
#include "f_finale.h"
#include "r_draw.h"
#include "console.h"
#include "i_video.h" // rendermode
#include "z_zone.h"
#include "m_misc.h"
#include "m_random.h"
#include "doomstat.h"
#ifdef HWRENDER
#include "hardware/hw_glob.h"
#endif
// SRB2Kart
#include "k_hud.h"
// Each screen is [vid.width*vid.height];
UINT8 *screens[5];
// screens[0] = main display window
// screens[1] = back screen, alternative blitting
// screens[2] = screenshot buffer, gif movie buffer
// screens[3] = fade screen start
// screens[4] = fade screen end, postimage tempoarary buffer
consvar_t cv_ticrate = {"showfps", "No", 0, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
static void CV_palette_OnChange(void);
static CV_PossibleValue_t gamma_cons_t[] = {{-15, "MIN"}, {5, "MAX"}, {0, NULL}};
consvar_t cv_globalgamma = {"gamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t saturation_cons_t[] = {{0, "MIN"}, {10, "MAX"}, {0, NULL}};
consvar_t cv_globalsaturation = {"saturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
#define huecoloursteps 4
static CV_PossibleValue_t hue_cons_t[] = {{0, "MIN"}, {(huecoloursteps*6)-1, "MAX"}, {0, NULL}};
consvar_t cv_rhue = {"rhue", "0", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_yhue = {"yhue", "4", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_ghue = {"ghue", "8", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_chue = {"chue", "12", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_bhue = {"bhue", "16", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_mhue = {"mhue", "20", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_rgamma = {"rgamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_ygamma = {"ygamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_ggamma = {"ggamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_cgamma = {"cgamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_bgamma = {"bgamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_mgamma = {"mgamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_rsaturation = {"rsaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_ysaturation = {"ysaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_gsaturation = {"gsaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_csaturation = {"csaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_bsaturation = {"bsaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_msaturation = {"msaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t constextsize_cons_t[] = {
{V_NOSCALEPATCH, "Small"}, {V_SMALLSCALEPATCH, "Medium"}, {V_MEDSCALEPATCH, "Large"}, {0, "Huge"},
{0, NULL}};
static void CV_constextsize_OnChange(void);
consvar_t cv_constextsize = {"con_textsize", "Medium", CV_SAVE|CV_CALL, constextsize_cons_t, CV_constextsize_OnChange, 0, NULL, NULL, 0, 0, NULL};
// local copy of the palette for V_GetColor()
RGBA_t *pLocalPalette = NULL;
RGBA_t *pMasterPalette = NULL;
/*
The following was an extremely helpful resource when developing my Colour Cube LUT.
http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter24.html
Please check it out if you're trying to maintain this.
toast 18/04/17
*/
float Cubepal[2][2][2][3];
boolean Cubeapply = false;
// returns whether to apply cube, selectively avoiding expensive operations
static boolean InitCube(void)
{
boolean apply = false;
UINT8 q;
float working[2][2][2][3] = // the initial positions of the corners of the colour cube!
{
{
{
{0.0, 0.0, 0.0}, // black corner
{0.0, 0.0, 1.0} // blue corner
},
{
{0.0, 1.0, 0.0}, // green corner
{0.0, 1.0, 1.0} // cyan corner
}
},
{
{
{1.0, 0.0, 0.0}, // red corner
{1.0, 0.0, 1.0} // magenta corner
},
{
{1.0, 1.0, 0.0}, // yellow corner
{1.0, 1.0, 1.0} // white corner
}
}
};
float desatur[3]; // grey
float globalgammamul, globalgammaoffs;
boolean doinggamma;
#define diffcons(cv) (cv.value != atoi(cv.defaultvalue))
doinggamma = diffcons(cv_globalgamma);
#define gammascale 8
globalgammamul = (cv_globalgamma.value ? ((255 - (gammascale*abs(cv_globalgamma.value)))/255.0) : 1.0);
globalgammaoffs = ((cv_globalgamma.value > 0) ? ((gammascale*cv_globalgamma.value)/255.0) : 0.0);
desatur[0] = desatur[1] = desatur[2] = globalgammaoffs + (0.33*globalgammamul);
if (doinggamma
|| diffcons(cv_rhue)
|| diffcons(cv_yhue)
|| diffcons(cv_ghue)
|| diffcons(cv_chue)
|| diffcons(cv_bhue)
|| diffcons(cv_mhue)
|| diffcons(cv_rgamma)
|| diffcons(cv_ygamma)
|| diffcons(cv_ggamma)
|| diffcons(cv_cgamma)
|| diffcons(cv_bgamma)
|| diffcons(cv_mgamma)) // set the gamma'd/hued positions (saturation is done later)
{
float mod, tempgammamul, tempgammaoffs;
apply = true;
working[0][0][0][0] = working[0][0][0][1] = working[0][0][0][2] = globalgammaoffs;
working[1][1][1][0] = working[1][1][1][1] = working[1][1][1][2] = globalgammaoffs+globalgammamul;
#define dohue(hue, gamma, loc) \
tempgammamul = (gamma ? ((255 - (gammascale*abs(gamma)))/255.0)*globalgammamul : globalgammamul);\
tempgammaoffs = ((gamma > 0) ? ((gammascale*gamma)/255.0) + globalgammaoffs : globalgammaoffs);\
mod = ((hue % huecoloursteps)*(tempgammamul)/huecoloursteps);\
switch (hue/huecoloursteps)\
{\
case 0:\
default:\
loc[0] = tempgammaoffs+tempgammamul;\
loc[1] = tempgammaoffs+mod;\
loc[2] = tempgammaoffs;\
break;\
case 1:\
loc[0] = tempgammaoffs+tempgammamul-mod;\
loc[1] = tempgammaoffs+tempgammamul;\
loc[2] = tempgammaoffs;\
break;\
case 2:\
loc[0] = tempgammaoffs;\
loc[1] = tempgammaoffs+tempgammamul;\
loc[2] = tempgammaoffs+mod;\
break;\
case 3:\
loc[0] = tempgammaoffs;\
loc[1] = tempgammaoffs+tempgammamul-mod;\
loc[2] = tempgammaoffs+tempgammamul;\
break;\
case 4:\
loc[0] = tempgammaoffs+mod;\
loc[1] = tempgammaoffs;\
loc[2] = tempgammaoffs+tempgammamul;\
break;\
case 5:\
loc[0] = tempgammaoffs+tempgammamul;\
loc[1] = tempgammaoffs;\
loc[2] = tempgammaoffs+tempgammamul-mod;\
break;\
}
dohue(cv_rhue.value, cv_rgamma.value, working[1][0][0]);
dohue(cv_yhue.value, cv_ygamma.value, working[1][1][0]);
dohue(cv_ghue.value, cv_ggamma.value, working[0][1][0]);
dohue(cv_chue.value, cv_cgamma.value, working[0][1][1]);
dohue(cv_bhue.value, cv_bgamma.value, working[0][0][1]);
dohue(cv_mhue.value, cv_mgamma.value, working[1][0][1]);
#undef dohue
}
#define dosaturation(a, e) a = ((1 - work)*e + work*a)
#define docvsat(cv_sat, hue, gamma, r, g, b) \
if diffcons(cv_sat)\
{\
float work, mod, tempgammamul, tempgammaoffs;\
apply = true;\
work = (cv_sat.value/10.0);\
mod = ((hue % huecoloursteps)*(1.0)/huecoloursteps);\
if (hue & huecoloursteps)\
mod = 2-mod;\
else\
mod += 1;\
tempgammamul = (gamma ? ((255 - (gammascale*abs(gamma)))/255.0)*globalgammamul : globalgammamul);\
tempgammaoffs = ((gamma > 0) ? ((gammascale*gamma)/255.0) + globalgammaoffs : globalgammaoffs);\
for (q = 0; q < 3; q++)\
dosaturation(working[r][g][b][q], (tempgammaoffs+(desatur[q]*mod*tempgammamul)));\
}
docvsat(cv_rsaturation, cv_rhue.value, cv_rgamma.value, 1, 0, 0);
docvsat(cv_ysaturation, cv_yhue.value, cv_ygamma.value, 1, 1, 0);
docvsat(cv_gsaturation, cv_ghue.value, cv_ggamma.value, 0, 1, 0);
docvsat(cv_csaturation, cv_chue.value, cv_cgamma.value, 0, 1, 1);
docvsat(cv_bsaturation, cv_bhue.value, cv_bgamma.value, 0, 0, 1);
docvsat(cv_msaturation, cv_mhue.value, cv_mgamma.value, 1, 0, 1);
#undef gammascale
if diffcons(cv_globalsaturation)
{
float work = (cv_globalsaturation.value/10.0);
apply = true;
for (q = 0; q < 3; q++)
{
dosaturation(working[1][0][0][q], desatur[q]);
dosaturation(working[0][1][0][q], desatur[q]);
dosaturation(working[0][0][1][q], desatur[q]);
dosaturation(working[1][1][0][q], 2*desatur[q]);
dosaturation(working[0][1][1][q], 2*desatur[q]);
dosaturation(working[1][0][1][q], 2*desatur[q]);
}
}
#undef dosaturation
#undef diffcons
if (!apply)
return false;
#define dowork(i, j, k, l) \
if (working[i][j][k][l] > 1.0)\
working[i][j][k][l] = 1.0;\
else if (working[i][j][k][l] < 0.0)\
working[i][j][k][l] = 0.0;\
Cubepal[i][j][k][l] = working[i][j][k][l]
for (q = 0; q < 3; q++)
{
dowork(0, 0, 0, q);
dowork(1, 0, 0, q);
dowork(0, 1, 0, q);
dowork(1, 1, 0, q);
dowork(0, 0, 1, q);
dowork(1, 0, 1, q);
dowork(0, 1, 1, q);
dowork(1, 1, 1, q);
}
#undef dowork
return true;
}
#ifdef BACKWARDSCOMPATCORRECTION
/*
So it turns out that the way gamma was implemented previously, the default
colour profile of the game was messed up. Since this bad decision has been
around for a long time, and the intent is to keep the base game looking the
same, I'm not gonna be the one to remove this base modification.
toast 20/04/17
... welp yes i am (27/07/19, see the ifdef around it)
*/
const UINT8 correctiontable[256] =
{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,
17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,
33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,
49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,
65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,
81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,
97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,
113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,
128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255};
#endif
// keep a copy of the palette so that we can get the RGB value for a color index at any time.
static void LoadPalette(const char *lumpname)
{
lumpnum_t lumpnum = W_GetNumForName(lumpname);
size_t i, palsize = W_LumpLength(lumpnum)/3;
UINT8 *pal;
Cubeapply = InitCube();
Z_Free(pLocalPalette);
Z_Free(pMasterPalette);
pLocalPalette = Z_Malloc(sizeof (*pLocalPalette)*palsize, PU_STATIC, NULL);
pMasterPalette = Z_Malloc(sizeof (*pMasterPalette)*palsize, PU_STATIC, NULL);
pal = W_CacheLumpNum(lumpnum, PU_CACHE);
for (i = 0; i < palsize; i++)
{
#ifdef BACKWARDSCOMPATCORRECTION
pMasterPalette[i].s.red = pLocalPalette[i].s.red = correctiontable[*pal++];
pMasterPalette[i].s.green = pLocalPalette[i].s.green = correctiontable[*pal++];
pMasterPalette[i].s.blue = pLocalPalette[i].s.blue = correctiontable[*pal++];
#else
pMasterPalette[i].s.red = pLocalPalette[i].s.red = *pal++;
pMasterPalette[i].s.green = pLocalPalette[i].s.green = *pal++;
pMasterPalette[i].s.blue = pLocalPalette[i].s.blue = *pal++;
#endif
pMasterPalette[i].s.alpha = pLocalPalette[i].s.alpha = 0xFF;
// lerp of colour cubing! if you want, make it smoother yourself
if (Cubeapply)
V_CubeApply(&pLocalPalette[i].s.red, &pLocalPalette[i].s.green, &pLocalPalette[i].s.blue);
}
}
void V_CubeApply(UINT8 *red, UINT8 *green, UINT8 *blue)
{
float working[4][3];
float linear;
UINT8 q;
if (!Cubeapply)
return;
linear = (*red/255.0);
#define dolerp(e1, e2) ((1 - linear)*e1 + linear*e2)
for (q = 0; q < 3; q++)
{
working[0][q] = dolerp(Cubepal[0][0][0][q], Cubepal[1][0][0][q]);
working[1][q] = dolerp(Cubepal[0][1][0][q], Cubepal[1][1][0][q]);
working[2][q] = dolerp(Cubepal[0][0][1][q], Cubepal[1][0][1][q]);
working[3][q] = dolerp(Cubepal[0][1][1][q], Cubepal[1][1][1][q]);
}
linear = (*green/255.0);
for (q = 0; q < 3; q++)
{
working[0][q] = dolerp(working[0][q], working[1][q]);
working[1][q] = dolerp(working[2][q], working[3][q]);
}
linear = (*blue/255.0);
for (q = 0; q < 3; q++)
{
working[0][q] = 255*dolerp(working[0][q], working[1][q]);
if (working[0][q] > 255.0)
working[0][q] = 255.0;
else if (working[0][q] < 0.0)
working[0][q] = 0.0;
}
#undef dolerp
*red = (UINT8)(working[0][0]);
*green = (UINT8)(working[0][1]);
*blue = (UINT8)(working[0][2]);
}
const char *R_GetPalname(UINT16 num)
{
static char palname[9];
char newpal[9] = "PLAYPAL";
if (num > 0 && num <= 10000)
snprintf(newpal, 8, "PAL%04u", num-1);
strncpy(palname, newpal, 8);
return palname;
}
const char *GetPalette(void)
{
if (gamestate == GS_LEVEL)
return R_GetPalname((encoremode ? mapheaderinfo[gamemap-1]->encorepal : mapheaderinfo[gamemap-1]->palette));
return "PLAYPAL";
}
static void LoadMapPalette(void)
{
LoadPalette(GetPalette());
}
// -------------+
// V_SetPalette : Set the current palette to use for palettized graphics
// :
// -------------+
void V_SetPalette(INT32 palettenum)
{
if (!pLocalPalette)
LoadMapPalette();
#ifdef HWRENDER
if (rendermode == render_opengl)
HWR_SetPalette(&pLocalPalette[palettenum*256]);
#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL)
else
#endif
#endif
if (rendermode != render_none)
I_SetPalette(&pLocalPalette[palettenum*256]);
}
void V_SetPaletteLump(const char *pal)
{
LoadPalette(pal);
#ifdef HWRENDER
if (rendermode == render_opengl)
HWR_SetPalette(pLocalPalette);
#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL)
else
#endif
#endif
if (rendermode != render_none)
I_SetPalette(pLocalPalette);
#ifdef HASINVERT
R_MakeInvertmap();
#endif
}
static void CV_palette_OnChange(void)
{
// reload palette
LoadMapPalette();
V_SetPalette(0);
}
#if defined (__GNUC__) && defined (__i386__) && !defined (NOASM) && !defined (__APPLE__) && !defined (NORUSEASM)
void VID_BlitLinearScreen_ASM(const UINT8 *srcptr, UINT8 *destptr, INT32 width, INT32 height, size_t srcrowbytes,
size_t destrowbytes);
#define HAVE_VIDCOPY
#endif
static void CV_constextsize_OnChange(void)
{
con_recalc = true;
}
// --------------------------------------------------------------------------
// Copy a rectangular area from one bitmap to another (8bpp)
// --------------------------------------------------------------------------
void VID_BlitLinearScreen(const UINT8 *srcptr, UINT8 *destptr, INT32 width, INT32 height, size_t srcrowbytes,
size_t destrowbytes)
{
#ifdef HAVE_VIDCOPY
VID_BlitLinearScreen_ASM(srcptr,destptr,width,height,srcrowbytes,destrowbytes);
#else
if ((srcrowbytes == destrowbytes) && (srcrowbytes == (size_t)width))
M_Memcpy(destptr, srcptr, srcrowbytes * height);
else
{
while (height--)
{
M_Memcpy(destptr, srcptr, width);
destptr += destrowbytes;
srcptr += srcrowbytes;
}
}
#endif
}
static UINT8 hudplusalpha[11] = { 10, 8, 6, 4, 2, 0, 0, 0, 0, 0, 0};
static UINT8 hudminusalpha[11] = { 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5};
UINT8 hudtrans = 0;
static const UINT8 *v_colormap = NULL;
static const UINT8 *v_translevel = NULL;
static inline UINT8 standardpdraw(const UINT8 *dest, const UINT8 *source, fixed_t ofs)
{
(void)dest; return source[ofs>>FRACBITS];
}
static inline UINT8 mappedpdraw(const UINT8 *dest, const UINT8 *source, fixed_t ofs)
{
(void)dest; return *(v_colormap + source[ofs>>FRACBITS]);
}
static inline UINT8 translucentpdraw(const UINT8 *dest, const UINT8 *source, fixed_t ofs)
{
return *(v_translevel + ((source[ofs>>FRACBITS]<<8)&0xff00) + (*dest&0xff));
}
static inline UINT8 transmappedpdraw(const UINT8 *dest, const UINT8 *source, fixed_t ofs)
{
return *(v_translevel + (((*(v_colormap + source[ofs>>FRACBITS]))<<8)&0xff00) + (*dest&0xff));
}
// Draws a patch scaled to arbitrary size.
void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 scrn, patch_t *patch, const UINT8 *colormap)
{
UINT8 (*patchdrawfunc)(const UINT8*, const UINT8*, fixed_t);
UINT32 alphalevel = 0;
fixed_t col, ofs, colfrac, rowfrac, fdup, vdup;
INT32 dupx, dupy;
const column_t *column;
UINT8 *desttop, *dest, *deststart, *destend;
const UINT8 *source, *deststop;
fixed_t pwidth; // patch width
fixed_t offx = 0; // x offset
if (rendermode == render_none)
return;
#ifdef HWRENDER
//if (rendermode != render_soft && !con_startup) // Why?
if (rendermode == render_opengl)
{
HWR_DrawStretchyFixedPatch((GLPatch_t *)patch, x, y, pscale, vscale, scrn, colormap);
return;
}
#endif
patchdrawfunc = standardpdraw;
v_translevel = NULL;
if ((alphalevel = ((scrn & V_ALPHAMASK) >> V_ALPHASHIFT)))
{
if (alphalevel == 13)
alphalevel = hudminusalpha[st_translucency];
else if (alphalevel == 14)
alphalevel = 10 - st_translucency;
else if (alphalevel == 15)
alphalevel = hudplusalpha[st_translucency];
if (alphalevel >= 10)
return; // invis
if (alphalevel)
{
v_translevel = transtables + ((alphalevel-1)<<FF_TRANSSHIFT);
patchdrawfunc = translucentpdraw;
}
}
v_colormap = NULL;
if (colormap)
{
v_colormap = colormap;
patchdrawfunc = (v_translevel) ? transmappedpdraw : mappedpdraw;
}
dupx = vid.dupx;
dupy = vid.dupy;
if (scrn & V_SCALEPATCHMASK) switch ((scrn & V_SCALEPATCHMASK) >> V_SCALEPATCHSHIFT)
{
case 1: // V_NOSCALEPATCH
dupx = dupy = 1;
break;
case 2: // V_SMALLSCALEPATCH
dupx = vid.smalldupx;
dupy = vid.smalldupy;
break;
case 3: // V_MEDSCALEPATCH
dupx = vid.meddupx;
dupy = vid.meddupy;
break;
default:
break;
}
// only use one dup, to avoid stretching (har har)
dupx = dupy = (dupx < dupy ? dupx : dupy);
fdup = vdup = FixedMul(dupx<<FRACBITS, pscale);
if (vscale != pscale)
vdup = FixedMul(dupx<<FRACBITS, vscale);
colfrac = FixedDiv(FRACUNIT, fdup);
rowfrac = FixedDiv(FRACUNIT, vdup);
// So it turns out offsets aren't scaled in V_NOSCALESTART unless V_OFFSET is applied ...poo, that's terrible
// For now let's just at least give V_OFFSET the ability to support V_FLIP
// I'll probably make a better fix for 2.2 where I don't have to worry about breaking existing support for stuff
// -- Monster Iestyn 29/10/18
{
fixed_t offsetx = 0, offsety = 0;
// left offset
if (scrn & V_FLIP)
offsetx = FixedMul((SHORT(patch->width) - SHORT(patch->leftoffset))<<FRACBITS, pscale) + 1;
else
offsetx = FixedMul(SHORT(patch->leftoffset)<<FRACBITS, pscale);
// top offset
// TODO: make some kind of vertical version of V_FLIP, maybe by deprecating V_OFFSET in future?!?
offsety = FixedMul(SHORT(patch->topoffset)<<FRACBITS, vscale);
if ((scrn & (V_NOSCALESTART|V_OFFSET)) == (V_NOSCALESTART|V_OFFSET)) // Multiply by dupx/dupy for crosshairs
{
offsetx = FixedMul(offsetx, dupx<<FRACBITS);
offsety = FixedMul(offsety, dupy<<FRACBITS);
}
// Subtract the offsets from x/y positions
x -= offsetx;
y -= offsety;
}
desttop = screens[scrn&V_PARAMMASK];
if (!desttop)
return;
deststop = desttop + vid.rowbytes * vid.height;
if (scrn & V_NOSCALESTART)
{
x >>= FRACBITS;
y >>= FRACBITS;
desttop += (y*vid.width) + x;
}
else
{
x = FixedMul(x,dupx<<FRACBITS);
y = FixedMul(y,dupy<<FRACBITS);
x >>= FRACBITS;
y >>= FRACBITS;
// Center it if necessary
if (!(scrn & V_SCALEPATCHMASK))
{
K_AdjustXYWithSnap(&x, &y, scrn, dupx, dupy);
}
desttop += (y*vid.width) + x;
}
if (pscale != FRACUNIT) // scale width properly
{
pwidth = SHORT(patch->width)<<FRACBITS;
pwidth = FixedMul(pwidth, pscale);
pwidth = FixedMul(pwidth, dupx<<FRACBITS);
pwidth >>= FRACBITS;
}
else
pwidth = SHORT(patch->width) * dupx;
deststart = desttop;
destend = desttop + pwidth;
for (col = 0; (col>>FRACBITS) < SHORT(patch->width); col += colfrac, ++offx, desttop++)
{
INT32 topdelta, prevdelta = -1;
if (scrn & V_FLIP) // offx is measured from right edge instead of left
{
if (x+pwidth-offx < 0) // don't draw off the left of the screen (WRAP PREVENTION)
break;
if (x+pwidth-offx >= vid.width) // don't draw off the right of the screen (WRAP PREVENTION)
continue;
}
else
{
if (x+offx < 0) // don't draw off the left of the screen (WRAP PREVENTION)
continue;
if (x+offx >= vid.width) // don't draw off the right of the screen (WRAP PREVENTION)
break;
}
column = (const column_t *)((const UINT8 *)(patch) + LONG(patch->columnofs[col>>FRACBITS]));
while (column->topdelta != 0xff)
{
topdelta = column->topdelta;
if (topdelta <= prevdelta)
topdelta += prevdelta;
prevdelta = topdelta;
source = (const UINT8 *)(column) + 3;
dest = desttop;
if (scrn & V_FLIP)
dest = deststart + (destend - desttop);
dest += FixedInt(FixedMul(topdelta<<FRACBITS,vdup))*vid.width;
for (ofs = 0; dest < deststop && (ofs>>FRACBITS) < column->length; ofs += rowfrac)
{
if (dest >= screens[scrn&V_PARAMMASK]) // don't draw off the top of the screen (CRASH PREVENTION)
*dest = patchdrawfunc(dest, source, ofs);
dest += vid.width;
}
column = (const column_t *)((const UINT8 *)column + column->length + 4);
}
}
}
// Draws a patch cropped and scaled to arbitrary size.
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)
{
UINT8 (*patchdrawfunc)(const UINT8*, const UINT8*, fixed_t);
UINT32 alphalevel = 0;
// boolean flip = false;
fixed_t col, ofs, colfrac, rowfrac, fdup;
INT32 dupx, dupy;
const column_t *column;
UINT8 *desttop, *dest;
const UINT8 *source, *deststop;
if (rendermode == render_none)
return;
#ifdef HWRENDER
//if (rendermode != render_soft && !con_startup) // Not this again
if (rendermode == render_opengl)
{
HWR_DrawCroppedPatch((GLPatch_t*)patch,x,y,pscale,scrn,sx,sy,w,h);
return;
}
#endif
patchdrawfunc = standardpdraw;
v_translevel = NULL;
if ((alphalevel = ((scrn & V_ALPHAMASK) >> V_ALPHASHIFT)))
{
if (alphalevel == 13)
alphalevel = hudminusalpha[st_translucency];
else if (alphalevel == 14)
alphalevel = 10 - st_translucency;
else if (alphalevel == 15)
alphalevel = hudplusalpha[st_translucency];
if (alphalevel >= 10)
return; // invis
if (alphalevel)
{
v_translevel = transtables + ((alphalevel-1)<<FF_TRANSSHIFT);
patchdrawfunc = translucentpdraw;
}
}
// only use one dup, to avoid stretching (har har)
dupx = dupy = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy);
fdup = FixedMul(dupx<<FRACBITS, pscale);
colfrac = FixedDiv(FRACUNIT, fdup);
rowfrac = FixedDiv(FRACUNIT, fdup);
y -= FixedMul(SHORT(patch->topoffset)<<FRACBITS, pscale);
x -= FixedMul(SHORT(patch->leftoffset)<<FRACBITS, pscale);
desttop = screens[scrn&V_PARAMMASK];
if (!desttop)
return;
deststop = desttop + vid.rowbytes * vid.height;
if (scrn & V_NOSCALESTART) {
x >>= FRACBITS;
y >>= FRACBITS;
desttop += (y*vid.width) + x;
}
else
{
x = FixedMul(x,dupx<<FRACBITS);
y = FixedMul(y,dupy<<FRACBITS);
x >>= FRACBITS;
y >>= FRACBITS;
// Center it if necessary
// adjustxy
desttop += (y*vid.width) + x;
}
for (col = sx<<FRACBITS; (col>>FRACBITS) < SHORT(patch->width) && ((col>>FRACBITS) - sx) < w; col += colfrac, ++x, desttop++)
{
INT32 topdelta, prevdelta = -1;
if (x < 0) // don't draw off the left of the screen (WRAP PREVENTION)
continue;
if (x >= vid.width) // don't draw off the right of the screen (WRAP PREVENTION)
break;
column = (const column_t *)((const UINT8 *)(patch) + LONG(patch->columnofs[col>>FRACBITS]));
while (column->topdelta != 0xff)
{
topdelta = column->topdelta;
if (topdelta <= prevdelta)
topdelta += prevdelta;
prevdelta = topdelta;
source = (const UINT8 *)(column) + 3;
dest = desttop;
if (topdelta-sy > 0)
{
dest += FixedInt(FixedMul((topdelta-sy)<<FRACBITS,fdup))*vid.width;
ofs = 0;
}
else
ofs = (sy-topdelta)<<FRACBITS;
for (; dest < deststop && (ofs>>FRACBITS) < column->length && (((ofs>>FRACBITS) - sy) + topdelta) < h; ofs += rowfrac)
{
if (dest >= screens[scrn&V_PARAMMASK]) // don't draw off the top of the screen (CRASH PREVENTION)
*dest = patchdrawfunc(dest, source, ofs);
dest += vid.width;
}
column = (const column_t *)((const UINT8 *)column + column->length + 4);
}
}
}
//
// V_DrawContinueIcon
// Draw a mini player! If we can, that is. Otherwise we draw a star.
//
void V_DrawContinueIcon(INT32 x, INT32 y, INT32 flags, INT32 skinnum, UINT16 skincolor)
{
(void)skinnum;
(void)skincolor;
V_DrawScaledPatch(x - 10, y - 14, flags, W_CachePatchName("CONTINS", PU_PATCH));
}
//
// V_DrawBlock
// Draw a linear block of pixels into the view buffer.
//
void V_DrawBlock(INT32 x, INT32 y, INT32 scrn, INT32 width, INT32 height, const UINT8 *src)
{
UINT8 *dest;
const UINT8 *deststop;
#ifdef RANGECHECK
if (x < 0 || x + width > vid.width || y < 0 || y + height > vid.height || (unsigned)scrn > 4)
I_Error("Bad V_DrawBlock");
#endif
dest = screens[scrn] + y*vid.width + x;
deststop = screens[scrn] + vid.rowbytes * vid.height;
while (height--)
{
M_Memcpy(dest, src, width);
src += width;
dest += vid.width;
if (dest > deststop)
return;
}
}
//
// Fills a box of pixels with a single color, NOTE: scaled to screen size
//
void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c)
{
UINT8 *dest;
const UINT8 *deststop;
if (rendermode == render_none)
return;
#ifdef HWRENDER
//if (rendermode != render_soft && !con_startup) // Not this again
if (rendermode == render_opengl)
{
HWR_DrawFill(x, y, w, h, c);
return;
}
#endif
if (!(c & V_NOSCALESTART))
{
INT32 dupx = vid.dupx, dupy = vid.dupy;
if (x == 0 && y == 0 && w == BASEVIDWIDTH && h == BASEVIDHEIGHT)
{ // Clear the entire screen, from dest to deststop. Yes, this really works.
memset(screens[0], (c&255), vid.width * vid.height * vid.bpp);
return;
}
x *= dupx;
y *= dupy;
w *= dupx;
h *= dupy;
// Center it if necessary
K_AdjustXYWithSnap(&x, &y, c, dupx, dupy);
}
if (x >= vid.width || y >= vid.height)
return; // off the screen
if (x < 0)
{
w += x;
x = 0;
}
if (y < 0)
{
h += y;
y = 0;
}
if (w <= 0 || h <= 0)
return; // zero width/height wouldn't draw anything
if (x + w > vid.width)
w = vid.width - x;
if (y + h > vid.height)
h = vid.height - y;
dest = screens[0] + y*vid.width + x;
deststop = screens[0] + vid.rowbytes * vid.height;
c &= 255;
for (;(--h >= 0) && dest < deststop; dest += vid.width)
memset(dest, c, w * vid.bpp);
}
#ifdef HWRENDER
// This is now a function since it's otherwise repeated 2 times and honestly looks retarded:
static UINT32 V_GetHWConsBackColor(void)
{
UINT32 hwcolor;
switch (cons_backcolor.value)
{
case 0: hwcolor = 0xffffff00; break; // White
case 1: hwcolor = 0x80808000; break; // Black
case 2: hwcolor = 0xdeb88700; break; // Sepia
case 3: hwcolor = 0x40201000; break; // Brown
case 4: hwcolor = 0xfa807200; break; // Pink
case 5: hwcolor = 0xff69b400; break; // Raspberry
case 6: hwcolor = 0xff000000; break; // Red
case 7: hwcolor = 0xffd68300; break; // Creamsicle
case 8: hwcolor = 0xff800000; break; // Orange
case 9: hwcolor = 0xdaa52000; break; // Gold
case 10: hwcolor = 0x80800000; break; // Yellow
case 11: hwcolor = 0x00ff0000; break; // Emerald
case 12: hwcolor = 0x00800000; break; // Green
case 13: hwcolor = 0x4080ff00; break; // Cyan
case 14: hwcolor = 0x4682b400; break; // Steel
case 15: hwcolor = 0x1e90ff00; break; // Periwinkle
case 16: hwcolor = 0x0000ff00; break; // Blue
case 17: hwcolor = 0xff00ff00; break; // Purple
case 18: hwcolor = 0xee82ee00; break; // Lavender
// Default green
default: hwcolor = 0x00800000; break;
}
return hwcolor;
}
#endif
// THANK YOU MPC!!!
// and thanks toaster for cleaning it up.
void V_DrawFillConsoleMap(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c)
{
UINT8 *dest;
const UINT8 *deststop;
INT32 u;
UINT8 *fadetable;
UINT32 alphalevel = 0;
if (rendermode == render_none)
return;
#ifdef HWRENDER
if (rendermode == render_opengl)
{
UINT32 hwcolor = V_GetHWConsBackColor();
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.
return;
}
#endif
if ((alphalevel = ((c & V_ALPHAMASK) >> V_ALPHASHIFT)))
{
if (alphalevel == 13)
alphalevel = hudminusalpha[st_translucency];
else if (alphalevel == 14)
alphalevel = 10 - st_translucency;
else if (alphalevel == 15)
alphalevel = hudplusalpha[st_translucency];
if (alphalevel >= 10)
return; // invis
}
if (!(c & V_NOSCALESTART))
{
INT32 dupx = vid.dupx, dupy = vid.dupy;
x *= dupx;
y *= dupy;
w *= dupx;
h *= dupy;
// adjustxy
}
if (x >= vid.width || y >= vid.height)
return; // off the screen
if (x < 0) {
w += x;
x = 0;
}
if (y < 0) {
h += y;
y = 0;
}
if (w <= 0 || h <= 0)
return; // zero width/height wouldn't draw anything
if (x + w > vid.width)
w = vid.width-x;
if (y + h > vid.height)
h = vid.height-y;
dest = screens[0] + y*vid.width + x;
deststop = screens[0] + vid.rowbytes * vid.height;
c &= 255;
// Jimita (12-04-2018)
if (alphalevel)
{
fadetable = ((UINT8 *)transtables + ((alphalevel-1)<<FF_TRANSSHIFT) + (c*256));
for (;(--h >= 0) && dest < deststop; dest += vid.width)
{
u = 0;
while (u < w)
{
dest[u] = fadetable[consolebgmap[dest[u]]];
u++;
}
}
}
else
{
for (;(--h >= 0) && dest < deststop; dest += vid.width)
{
u = 0;
while (u < w)
{
dest[u] = consolebgmap[dest[u]];
u++;
}
}
}
}
//
// Fills a triangle of pixels with a single color, NOTE: scaled to screen size
//
// ...
// .. <-- this shape only for now, i'm afraid
// .
//
void V_DrawDiag(INT32 x, INT32 y, INT32 wh, INT32 c)
{
UINT8 *dest;
const UINT8 *deststop;
INT32 w, h, wait = 0;
if (rendermode == render_none)
return;
#ifdef HWRENDER
if (rendermode != render_soft && !con_startup)
{
HWR_DrawDiag(x, y, wh, c);
return;
}
#endif
if (!(c & V_NOSCALESTART))
{
INT32 dupx = vid.dupx, dupy = vid.dupy;
x *= dupx;
y *= dupy;
wh *= dupx;
// Center it if necessary
K_AdjustXYWithSnap(&x, &y, c, dupx, dupy);
}
if (x >= vid.width || y >= vid.height)
return; // off the screen
if (y < 0)
{
wh += y;
y = 0;
}
w = h = wh;
if (x < 0)
{
w += x;
x = 0;
}
if (w <= 0 || h <= 0)
return; // zero width/height wouldn't draw anything
if (x + w > vid.width)
{
wait = w - (vid.width - x);
w = vid.width - x;
}
if (y + w > vid.height)
h = vid.height - y;
if (h > w)
h = w;
dest = screens[0] + y*vid.width + x;
deststop = screens[0] + vid.rowbytes * vid.height;
c &= 255;
for (;(--h >= 0) && dest < deststop; dest += vid.width)
{
memset(dest, c, w * vid.bpp);
if (wait)
wait--;
else
w--;
}
}
//
// If color is 0x00 to 0xFF, draw transtable (strength range 0-9).
// Else, use COLORMAP lump (strength range 0-31).
// c is not color, it is for flags only. transparency flags will be ignored.
// IF YOU ARE NOT CAREFUL, THIS CAN AND WILL CRASH!
// I have kept the safety checks for strength out of this function;
// I don't trust Lua users with it, so it doesn't matter.
//
void V_DrawFadeFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c, UINT16 color, UINT8 strength)
{
UINT8 *dest;
const UINT8 *deststop;
INT32 u;
UINT8 *fadetable;
if (rendermode == render_none)
return;
#ifdef HWRENDER
if (rendermode == render_opengl)
{
// ughhhhh please can someone else do this? thanks ~toast 25/7/19 in 38 degrees centigrade w/o AC
HWR_DrawFadeFill(x, y, w, h, c, color, strength); // toast two days later - left above comment in 'cause it's funny
return;
}
#endif
if (!(c & V_NOSCALESTART))
{
INT32 dupx = vid.dupx, dupy = vid.dupy;
x *= dupx;
y *= dupy;
w *= dupx;
h *= dupy;
// Center it if necessary
// adjustxy
}
if (x >= vid.width || y >= vid.height)
return; // off the screen
if (x < 0) {
w += x;
x = 0;
}
if (y < 0) {
h += y;
y = 0;
}
if (w <= 0 || h <= 0)
return; // zero width/height wouldn't draw anything
if (x + w > vid.width)
w = vid.width-x;
if (y + h > vid.height)
h = vid.height-y;
dest = screens[0] + y*vid.width + x;
deststop = screens[0] + vid.rowbytes * vid.height;
c &= 255;
fadetable = ((color & 0xFF00) // Color is not palette index?
? ((UINT8 *)colormaps + strength*256) // Do COLORMAP fade.
: ((UINT8 *)transtables + ((9-strength)<<FF_TRANSSHIFT) + color*256)); // Else, do TRANSMAP** fade.
for (;(--h >= 0) && dest < deststop; dest += vid.width)
{
u = 0;
while (u < w)
{
dest[u] = fadetable[dest[u]];
u++;
}
}
}
//
// Fills a box of pixels using a flat texture as a pattern, scaled to screen size.
//
void V_DrawFlatFill(INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatnum)
{
INT32 u, v, dupx, dupy;
fixed_t dx, dy, xfrac, yfrac;
const UINT8 *src, *deststop;
UINT8 *flat, *dest;
size_t size, lflatsize, flatshift;
#ifdef HWRENDER
if (rendermode == render_opengl)
{
HWR_DrawFlatFill(x, y, w, h, flatnum);
return;
}
#endif
size = W_LumpLength(flatnum);
switch (size)
{
case 4194304: // 2048x2048 lump
lflatsize = 2048;
flatshift = 10;
break;
case 1048576: // 1024x1024 lump
lflatsize = 1024;
flatshift = 9;
break;
case 262144:// 512x512 lump
lflatsize = 512;
flatshift = 8;
break;
case 65536: // 256x256 lump
lflatsize = 256;
flatshift = 7;
break;
case 16384: // 128x128 lump
lflatsize = 128;
flatshift = 7;
break;
case 1024: // 32x32 lump
lflatsize = 32;
flatshift = 5;
break;
default: // 64x64 lump
lflatsize = 64;
flatshift = 6;
break;
}
flat = W_CacheLumpNum(flatnum, PU_CACHE);
dupx = dupy = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy);
dest = screens[0] + y*dupy*vid.width + x*dupx;
deststop = screens[0] + vid.rowbytes * vid.height;
// from V_DrawScaledPatch
if (vid.width != BASEVIDWIDTH * dupx)
{
// dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
// so center this imaginary screen
dest += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
}
if (vid.height != BASEVIDHEIGHT * dupy)
{
// same thing here
dest += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width / 2;
}
w *= dupx;
h *= dupy;
dx = FixedDiv(FRACUNIT, dupx<<(FRACBITS-2));
dy = FixedDiv(FRACUNIT, dupy<<(FRACBITS-2));
yfrac = 0;
for (v = 0; v < h; v++, dest += vid.width)
{
xfrac = 0;
src = flat + (((yfrac>>FRACBITS) & (lflatsize - 1)) << flatshift);
for (u = 0; u < w; u++)
{
if (&dest[u] > deststop)
return;
dest[u] = src[(xfrac>>FRACBITS)&(lflatsize-1)];
xfrac += dx;
}
yfrac += dy;
}
}
//
// V_DrawPatchFill
//
void V_DrawPatchFill(patch_t *pat)
{
INT32 dupz = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy);
INT32 x, y, pw = SHORT(pat->width) * dupz, ph = SHORT(pat->height) * dupz;
for (x = 0; x < vid.width; x += pw)
{
for (y = 0; y < vid.height; y += ph)
V_DrawScaledPatch(x, y, V_NOSCALESTART, pat);
}
}
void V_DrawVhsEffect(boolean rewind)
{
static fixed_t upbary = 100, downbary = 150;
UINT8 *buf = screens[0], *tmp = screens[4];
UINT16 y;
UINT32 x, pos = 0;
UINT8 *normalmapstart = ((UINT8 *)transtables + (8<<FF_TRANSSHIFT|(19<<8)));
#ifdef HQ_VHS
UINT8 *tmapstart = ((UINT8 *)transtables + (6<<FF_TRANSSHIFT));
#endif
UINT8 *thismapstart;
SINT8 offs;
UINT8 barsize = vid.dupy<<5;
UINT8 updistort = vid.dupx<<(rewind ? 5 : 3);
UINT8 downdistort = updistort>>1;
if (rewind)
V_DrawVhsEffect(false); // experimentation
upbary -= vid.dupy * (rewind ? 3 : 1.8f);
downbary += vid.dupy * (rewind ? 2 : 1);
if (upbary < -barsize) upbary = vid.height;
if (downbary > vid.height) downbary = -barsize;
for (y = 0; y < vid.height; y+=2)
{
thismapstart = normalmapstart;
offs = 0;
if (y >= upbary && y < upbary+barsize)
{
thismapstart -= (2<<FF_TRANSSHIFT) - (5<<8);
offs += updistort * 2.0f * min(y-upbary, upbary+barsize-y) / barsize;
}
if (y >= downbary && y < downbary+barsize)
{
thismapstart -= (2<<FF_TRANSSHIFT) - (5<<8);
offs -= downdistort * 2.0f * min(y-downbary, downbary+barsize-y) / barsize;
}
offs += M_RandomKey(vid.dupx<<1);
// lazy way to avoid crashes
if (y == 0 && offs < 0) offs = 0;
else if (y >= vid.height-2 && offs > 0) offs = 0;
for (x = pos+vid.rowbytes*2; pos < x; pos++)
{
tmp[pos] = thismapstart[buf[pos+offs]];
#ifdef HQ_VHS
tmp[pos] = tmapstart[buf[pos]<<8 | tmp[pos]];
#endif
}
}
memcpy(buf, tmp, vid.rowbytes*vid.height);
}
//
// Fade all the screen buffer, so that the menu is more readable,
// especially now that we use the small hufont in the menus...
// If color is 0x00 to 0xFF, draw transtable (strength range 0-9).
// Else, use COLORMAP lump (strength range 0-31).
// IF YOU ARE NOT CAREFUL, THIS CAN AND WILL CRASH!
// I have kept the safety checks out of this function;
// the v.fadeScreen Lua interface handles those.
//
void V_DrawFadeScreen(UINT16 color, UINT8 strength)
{
#ifdef HWRENDER
if (rendermode == render_opengl)
{
HWR_FadeScreenMenuBack(color, strength);
return;
}
#endif
{
const UINT8 *fadetable =
(color > 0xFFF0) // Grab a specific colormap palette?
? R_GetTranslationColormap(color | 0xFFFF0000, strength, GTC_CACHE)
: ((color & 0xFF00) // Color is not palette index?
? ((UINT8 *)colormaps + strength*256) // Do COLORMAP fade.
: ((UINT8 *)transtables + ((9-strength)<<FF_TRANSSHIFT) + color*256)); // Else, do TRANSMAP** fade.
const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
UINT8 *buf = screens[0];
// heavily simplified -- we don't need to know x or y
// position when we're doing a full screen fade
for (; buf < deststop; ++buf)
*buf = fadetable[*buf];
}
}
//
// Fade the screen buffer, using a custom COLORMAP lump.
// Split from V_DrawFadeScreen, because that function has
// WAY too many options piled on top of it as is. :V
//
void V_DrawCustomFadeScreen(const char *lump, UINT8 strength)
{
#ifdef HWRENDER
if (rendermode != render_soft && rendermode != render_none)
{
//HWR_DrawCustomFadeScreen(color, strength);
return;
}
#endif
{
lumpnum_t lumpnum = LUMPERROR;
lighttable_t *clm = NULL;
if (lump != NULL)
lumpnum = W_GetNumForName(lump);
else
return;
if (lumpnum != LUMPERROR)
{
clm = Z_MallocAlign((256 * 32), PU_STATIC, NULL, 8);
W_ReadLump(lumpnum, clm);
if (clm != NULL)
{
const UINT8 *fadetable = ((UINT8 *)clm + strength*256);
const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
UINT8 *buf = screens[0];
// heavily simplified -- we don't need to know x or y
// position when we're doing a full screen fade
for (; buf < deststop; ++buf)
*buf = fadetable[*buf];
Z_Free(clm);
clm = NULL;
}
}
}
}
// Simple translucency with one color, over a set number of lines starting from the top.
void V_DrawFadeConsBack(INT32 plines)
{
UINT8 *deststop, *buf;
#ifdef HWRENDER // not win32 only 19990829 by Kin
if (rendermode == render_opengl)
{
UINT32 hwcolor = V_GetHWConsBackColor();
HWR_DrawConsoleBack(hwcolor, plines);
return;
}
#endif
// heavily simplified -- we don't need to know x or y position,
// just the stop position
deststop = screens[0] + vid.rowbytes * min(plines, vid.height);
for (buf = screens[0]; buf < deststop; ++buf)
*buf = consolebgmap[*buf];
}
//
// Invert the entire screen, for Encore fades
//
void V_EncoreInvertScreen(void)
{
#ifdef HWRENDER
if (rendermode != render_soft && rendermode != render_none)
{
//HWR_EncoreInvertScreen();
return;
}
#endif
{
const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
UINT8 *buf = screens[0];
for (; buf < deststop; ++buf)
{
*buf = NearestColor(
256 - pLocalPalette[*buf].s.red,
256 - pLocalPalette[*buf].s.green,
256 - pLocalPalette[*buf].s.blue
);
}
}
}
// Very similar to F_DrawFadeConsBack, except we draw from the middle(-ish) of the screen to the bottom.
void V_DrawPromptBack(INT32 boxheight, INT32 color)
{
UINT8 *deststop, *buf;
if (color >= 256 && color < 512)
{
if (boxheight < 0)
boxheight = -boxheight;
else // 4 lines of space plus gaps between and some leeway
boxheight = ((boxheight * 4) + (boxheight/2)*5);
V_DrawFill((BASEVIDWIDTH-(vid.width/vid.dupx))/2, BASEVIDHEIGHT-boxheight, (vid.width/vid.dupx),boxheight, (color-256)|V_SNAPTOBOTTOM);
return;
}
boxheight *= vid.dupy;
if (color == INT32_MAX)
color = cons_backcolor.value;
#ifdef HWRENDER
if (rendermode == render_opengl)
{
UINT32 hwcolor;
switch (color)
{
case 0: hwcolor = 0xffffff00; break; // White
case 1: hwcolor = 0x00000000; break; // Black // Note this is different from V_DrawFadeConsBack
case 2: hwcolor = 0xdeb88700; break; // Sepia
case 3: hwcolor = 0x40201000; break; // Brown
case 4: hwcolor = 0xfa807200; break; // Pink
case 5: hwcolor = 0xff69b400; break; // Raspberry
case 6: hwcolor = 0xff000000; break; // Red
case 7: hwcolor = 0xffd68300; break; // Creamsicle
case 8: hwcolor = 0xff800000; break; // Orange
case 9: hwcolor = 0xdaa52000; break; // Gold
case 10: hwcolor = 0x80800000; break; // Yellow
case 11: hwcolor = 0x00ff0000; break; // Emerald
case 12: hwcolor = 0x00800000; break; // Green
case 13: hwcolor = 0x4080ff00; break; // Cyan
case 14: hwcolor = 0x4682b400; break; // Steel
case 15: hwcolor = 0x1e90ff00; break; // Periwinkle
case 16: hwcolor = 0x0000ff00; break; // Blue
case 17: hwcolor = 0xff00ff00; break; // Purple
case 18: hwcolor = 0xee82ee00; break; // Lavender
// Default green
default: hwcolor = 0x00800000; break;
}
HWR_DrawTutorialBack(hwcolor, boxheight);
return;
}
#endif
CON_SetupBackColormapEx(color, true);
// heavily simplified -- we don't need to know x or y position,
// just the start and stop positions
buf = deststop = screens[0] + vid.rowbytes * vid.height;
if (boxheight < 0)
buf += vid.rowbytes * boxheight;
else // 4 lines of space plus gaps between and some leeway
buf -= vid.rowbytes * ((boxheight * 4) + (boxheight/2)*5);
for (; buf < deststop; ++buf)
*buf = promptbgmap[*buf];
}
// Gets string colormap, used for 0x80 color codes
//
UINT8 *V_GetStringColormap(INT32 colorflags)
{
#if 0 // perfect
switch ((colorflags & V_CHARCOLORMASK) >> V_CHARCOLORSHIFT)
{
case 1: // 0x81, purple
return purplemap;
case 2: // 0x82, yellow
return yellowmap;
case 3: // 0x83, green
return greenmap;
case 4: // 0x84, blue
return bluemap;
case 5: // 0x85, red
return redmap;
case 6: // 0x86, gray
return graymap;
case 7: // 0x87, orange
return orangemap;
case 8: // 0x88, sky
return skymap;
case 9: // 0x89, lavender
return lavendermap;
case 10: // 0x8A, gold
return goldmap;
case 11: // 0x8B, aqua-green
return aquamap;
case 12: // 0x8C, magenta
return magentamap;
case 13: // 0x8D, pink
return pinkmap;
case 14: // 0x8E, brown
return brownmap;
case 15: // 0x8F, tan
return tanmap;
default: // reset
return NULL;
}
#else // optimised
colorflags = ((colorflags & V_CHARCOLORMASK) >> V_CHARCOLORSHIFT);
if (!colorflags || colorflags > 15) // INT32 is signed, but V_CHARCOLORMASK is a very restrictive mask.
return NULL;
return (purplemap+((colorflags-1)<<8));
#endif
}
// Writes a single character (draw WHITE if bit 7 set)
//
void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed)
{
INT32 w, flags;
const UINT8 *colormap = V_GetStringColormap(c);
flags = c & ~(V_CHARCOLORMASK | V_PARAMMASK);
c &= 0x7f;
if (lowercaseallowed)
c -= HU_FONTSTART;
else
c = toupper(c) - HU_FONTSTART;
if (c < 0 || c >= HU_FONTSIZE || !fontv[HU_FONT].font[c])
return;
w = SHORT(fontv[HU_FONT].font[c]->width);
if (x + w > vid.width)
return;
if (colormap != NULL)
V_DrawMappedPatch(x, y, flags, fontv[HU_FONT].font[c], colormap);
else
V_DrawScaledPatch(x, y, flags, fontv[HU_FONT].font[c]);
}
// Writes a single character for the chat (half scaled). (draw WHITE if bit 7 set)
// 16/02/19: Scratch the scaling thing, chat doesn't work anymore under 2x res -Lat'
//
void V_DrawChatCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed, UINT8 *colormap)
{
INT32 w, flags;
//const UINT8 *colormap = V_GetStringColormap(c);
flags = c & ~(V_CHARCOLORMASK | V_PARAMMASK);
c &= 0x7f;
if (lowercaseallowed)
c -= HU_FONTSTART;
else
c = toupper(c) - HU_FONTSTART;
if (c < 0 || c >= HU_FONTSIZE || !fontv[HU_FONT].font[c])
return;
w = SHORT(fontv[HU_FONT].font[c]->width)/2;
if (x + w > vid.width)
return;
V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT/2, flags, fontv[HU_FONT].font[c], colormap);
}
// Precompile a wordwrapped string to any given width.
// This is a muuuch better method than V_WORDWRAP.
char *V_WordWrap(INT32 x, INT32 w, INT32 option, const char *string)
{
int c;
size_t chw, i, lastusablespace = 0;
size_t slen;
char *newstring = Z_StrDup(string);
INT32 spacewidth = 4, charwidth = 0;
slen = strlen(string);
if (w == 0)
w = BASEVIDWIDTH;
w -= x;
x = 0;
switch (option & V_SPACINGMASK)
{
case V_MONOSPACE:
spacewidth = 8;
/* FALLTHRU */
case V_OLDSPACING:
charwidth = 8;
break;
case V_6WIDTHSPACE:
spacewidth = 6;
default:
break;
}
for (i = 0; i < slen; ++i)
{
c = newstring[i];
if ((UINT8)c & 0x80) //color parsing! -Inuyasha 2.16.09
continue;
if (c == '\n')
{
x = 0;
lastusablespace = 0;
continue;
}
if (!(option & V_ALLOWLOWERCASE))
c = toupper(c);
c -= HU_FONTSTART;
if (c < 0 || c >= HU_FONTSIZE || !fontv[HU_FONT].font[c])
{
chw = spacewidth;
lastusablespace = i;
}
else
chw = (charwidth ? charwidth : fontv[HU_FONT].font[c]->width);
x += chw;
if (lastusablespace != 0 && x > w)
{
newstring[lastusablespace] = '\n';
i = lastusablespace;
lastusablespace = 0;
x = 0;
}
}
return newstring;
}
static inline fixed_t FixedCharacterDim(
fixed_t scale,
fixed_t chw,
INT32 hchw,
INT32 dupx,
fixed_t * cwp)
{
(void)scale;
(void)hchw;
(void)dupx;
(*cwp) = chw;
return 0;
}
static inline fixed_t VariableCharacterDim(
fixed_t scale,
fixed_t chw,
INT32 hchw,
INT32 dupx,
fixed_t * cwp)
{
(void)chw;
(void)hchw;
(void)dupx;
(*cwp) = FixedMul ((*cwp) << FRACBITS, scale);
return 0;
}
static inline fixed_t CenteredCharacterDim(
fixed_t scale,
fixed_t chw,
INT32 hchw,
INT32 dupx,
fixed_t * cwp)
{
INT32 cxoff;
/*
For example, center a 4 wide patch to 8 width:
4/2 = 2
8/2 = 4
4 - 2 = 2 (our offset)
2 + 4 = 6 = 8 - 2 (equal space on either side)
*/
cxoff = hchw -((*cwp) >> 1 );
(*cwp) = chw;
return FixedMul (( cxoff * dupx )<< FRACBITS, scale);
}
static inline fixed_t BunchedCharacterDim(
fixed_t scale,
fixed_t chw,
INT32 hchw,
INT32 dupx,
fixed_t * cwp)
{
(void)chw;
(void)hchw;
(void)dupx;
(*cwp) = FixedMul (max (1, (*cwp) - 1) << FRACBITS, scale);
return 0;
}
void V_DrawStringScaled(
fixed_t x,
fixed_t y,
fixed_t scale,
fixed_t spacescale,
fixed_t lfscale,
INT32 flags,
int fontno,
const char *s)
{
fixed_t chw;
INT32 hchw;/* half-width for centering */
fixed_t spacew;
fixed_t lfh;
INT32 dupx;
fixed_t right;
fixed_t bot;
fixed_t (*dim_fn)(fixed_t,fixed_t,INT32,INT32,fixed_t *);
font_t *font;
boolean uppercase;
boolean notcolored;
const UINT8 *colormap;
fixed_t cx, cy;
fixed_t cxoff;
fixed_t cw;
INT32 spacing;
fixed_t left;
int c;
uppercase = !( flags & V_ALLOWLOWERCASE );
flags &= ~(V_FLIP);/* These two (V_ALLOWLOWERCASE) share a bit. */
colormap = V_GetStringColormap(( flags & V_CHARCOLORMASK ));
notcolored = !colormap;
font = &fontv[fontno];
chw = 0;
spacing = ( flags & V_SPACINGMASK );
/*
Hardcoded until a better system can be implemented
for determining how fonts space.
*/
switch (fontno)
{
default:
case HU_FONT:
spacew = 4;
switch (spacing)
{
case V_MONOSPACE:
spacew = 8;
/* FALLTHRU */
case V_OLDSPACING:
chw = 8;
break;
case V_6WIDTHSPACE:
spacew = 6;
}
break;
case TINY_FONT:
spacew = 2;
switch (spacing)
{
case V_MONOSPACE:
spacew = 5;
/* FALLTHRU */
case V_OLDSPACING:
chw = 5;
break;
// Out of video flags, so we're reusing this for alternate charwidth instead
/*case V_6WIDTHSPACE:
spacewidth = 3;*/
}
break;
case KART_FONT:
spacew = 12;
switch (spacing)
{
case V_MONOSPACE:
spacew = 12;
/* FALLTHRU */
case V_OLDSPACING:
chw = 12;
break;
case V_6WIDTHSPACE:
spacew = 6;
}
break;
case LT_FONT:
spacew = 12;
break;
case CRED_FONT:
spacew = 16;
break;
}
switch (fontno)
{
default:
case HU_FONT:
case TINY_FONT:
case KART_FONT:
if (( flags & V_RETURN8 ))
lfh = 8;
else
lfh = 12;
break;
case LT_FONT:
case CRED_FONT:
lfh = 12;
break;
}
hchw = chw >> 1;
chw <<= FRACBITS;
spacew <<= FRACBITS;
lfh <<= FRACBITS;
#define Mul( id, scale ) ( id = FixedMul (scale, id) )
Mul (chw, scale);
Mul (spacew, scale);
Mul (lfh, scale);
Mul (spacew, spacescale);
Mul (lfh, lfscale);
#undef Mul
if (( flags & V_NOSCALESTART ))
{
dupx = vid.dupx;
hchw *= dupx;
chw *= dupx;
spacew *= dupx;
lfh *= vid.dupy;
right = vid.width;
}
else
{
dupx = 1;
right = ( vid.width / vid.dupx );
if (!( flags & V_SNAPTOLEFT ))
{
left = ( right - BASEVIDWIDTH )/ 2;/* left edge of drawable area */
right -= left;
}
}
right <<= FRACBITS;
bot = vid.height << FRACBITS;
if (fontno == TINY_FONT)
{
if (chw)
dim_fn = FixedCharacterDim;
else
{
/* Reuse this flag for the alternate bunched-up spacing. */
if (( flags & V_6WIDTHSPACE ))
dim_fn = BunchedCharacterDim;
else
dim_fn = VariableCharacterDim;
}
}
else
{
if (chw)
dim_fn = CenteredCharacterDim;
else
dim_fn = VariableCharacterDim;
}
cx = x;
cy = y;
for (; ( c = *s ); ++s)
{
switch (c)
{
case '\n':
cy += lfh;
if (cy >= bot)
return;
cx = x;
break;
default:
if (( c & 0x80 ))
{
if (notcolored)
{
colormap = V_GetStringColormap(
( ( c & 0x7f )<< V_CHARCOLORSHIFT )&
V_CHARCOLORMASK);
}
}
else if (cx < right)
{
if (uppercase)
c = toupper(c);
c -= font->start;
if (c >= 0 && c < font->size && font->font[c])
{
cw = SHORT (font->font[c]->width) * dupx;
cxoff = (*dim_fn)(scale, chw, hchw, dupx, &cw);
V_DrawFixedPatch(cx + cxoff, cy, scale,
flags, font->font[c], colormap);
cx += cw;
}
else
cx += spacew;
}
}
}
}
//
void V_DrawCenteredString(INT32 x, INT32 y, INT32 option, const char *string)
{
x -= V_StringWidth(string, option)/2;
V_DrawString(x, y, option, string);
}
void V_DrawRightAlignedString(INT32 x, INT32 y, INT32 option, const char *string)
{
x -= V_StringWidth(string, option);
V_DrawString(x, y, option, string);
}
void V_DrawCenteredSmallString(INT32 x, INT32 y, INT32 option, const char *string)
{
x -= V_SmallStringWidth(string, option)/2;
V_DrawSmallString(x, y, option, string);
}
void V_DrawRightAlignedSmallString(INT32 x, INT32 y, INT32 option, const char *string)
{
x -= V_SmallStringWidth(string, option);
V_DrawSmallString(x, y, option, string);
}
void V_DrawCenteredThinString(INT32 x, INT32 y, INT32 option, const char *string)
{
x -= V_ThinStringWidth(string, option)/2;
V_DrawThinString(x, y, option, string);
}
void V_DrawRightAlignedThinString(INT32 x, INT32 y, INT32 option, const char *string)
{
x -= V_ThinStringWidth(string, option);
V_DrawThinString(x, y, option, string);
}
// Draws a number using the PING font thingy.
// TODO: Merge number drawing functions into one with "font name" selection.
void V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *colormap)
{
INT32 w = SHORT(fontv[PINGNUM_FONT].font[0]->width); // this SHOULD always be 5 but I guess custom graphics exist.
if (flags & V_NOSCALESTART)
w *= vid.dupx;
if (num < 0)
num = -num;
// draw the number
do
{
x -= (w-1); // Oni wanted their outline to intersect.
V_DrawFixedPatch(x<<FRACBITS, y<<FRACBITS, FRACUNIT, flags, fontv[PINGNUM_FONT].font[num%10], colormap);
num /= 10;
} while (num);
}
// Find string width from cred_font chars
//
INT32 V_CreditStringWidth(const char *string)
{
INT32 c, w = 0;
size_t i;
// It's possible for string to be a null pointer
if (!string)
return 0;
for (i = 0; i < strlen(string); i++)
{
c = toupper(string[i]) - CRED_FONTSTART;
if (c < 0 || c >= CRED_FONTSIZE)
w += 16;
else
w += SHORT(fontv[CRED_FONT].font[c]->width);
}
return w;
}
// Draws a tallnum. Replaces two functions in y_inter and st_stuff
void V_DrawTallNum(INT32 x, INT32 y, INT32 flags, INT32 num)
{
INT32 w = SHORT(fontv[TALLNUM_FONT].font[0]->width);
boolean neg;
if (flags & V_NOSCALESTART)
w *= vid.dupx;
if ((neg = num < 0))
num = -num;
// draw the number
do
{
x -= w;
V_DrawScaledPatch(x, y, flags, fontv[TALLNUM_FONT].font[num % 10]);
num /= 10;
} while (num);
// draw a minus sign if necessary
if (neg)
V_DrawScaledPatch(x - w, y, flags, tallminus); // Tails
}
// Draws a number with a set number of digits.
// Does not handle negative numbers in a special way, don't try to feed it any.
void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits)
{
INT32 w = SHORT(fontv[TALLNUM_FONT].font[0]->width);
if (flags & V_NOSCALESTART)
w *= vid.dupx;
if (num < 0)
num = -num;
// draw the number
do
{
x -= w;
V_DrawScaledPatch(x, y, flags, fontv[TALLNUM_FONT].font[num % 10]);
num /= 10;
} while (--digits);
}
// Find string width from lt_font chars
//
INT32 V_LevelNameWidth(const char *string)
{
INT32 c, w = 0;
size_t i;
for (i = 0; i < strlen(string); i++)
{
if (string[i] & 0x80)
continue;
c = string[i] - LT_FONTSTART;
if (c < 0 || c >= LT_FONTSIZE || !fontv[LT_FONT].font[c])
w += 12;
else
w += SHORT(fontv[LT_FONT].font[c]->width);
}
return w;
}
// Find max height of the string
//
INT32 V_LevelNameHeight(const char *string)
{
INT32 c, w = 0;
size_t i;
for (i = 0; i < strlen(string); i++)
{
c = string[i] - LT_FONTSTART;
if (c < 0 || c >= LT_FONTSIZE || !fontv[LT_FONT].font[c])
continue;
if (SHORT(fontv[LT_FONT].font[c]->height) > w)
w = SHORT(fontv[LT_FONT].font[c]->height);
}
return w;
}
//
// Find string width from hu_font chars
//
INT32 V_StringWidth(const char *string, INT32 option)
{
INT32 c, w = 0;
INT32 spacewidth = 4, charwidth = 0;
size_t i;
switch (option & V_SPACINGMASK)
{
case V_MONOSPACE:
spacewidth = 8;
/* FALLTHRU */
case V_OLDSPACING:
charwidth = 8;
break;
case V_6WIDTHSPACE:
spacewidth = 6;
default:
break;
}
for (i = 0; i < strlen(string); i++)
{
c = string[i];
if ((UINT8)c & 0x80) //color parsing! -Inuyasha 2.16.09
continue;
c = toupper(c) - HU_FONTSTART;
if (c < 0 || c >= HU_FONTSIZE || !fontv[HU_FONT].font[c])
w += spacewidth;
else
w += (charwidth ? charwidth : SHORT(fontv[HU_FONT].font[c]->width));
}
if (option & V_NOSCALESTART)
w *= vid.dupx;
return w;
}
//
// Find string width from hu_font chars, 0.5x scale
//
INT32 V_SmallStringWidth(const char *string, INT32 option)
{
INT32 c, w = 0;
INT32 spacewidth = 2, charwidth = 0;
size_t i;
switch (option & V_SPACINGMASK)
{
case V_MONOSPACE:
spacewidth = 4;
/* FALLTHRU */
case V_OLDSPACING:
charwidth = 4;
break;
case V_6WIDTHSPACE:
spacewidth = 3;
default:
break;
}
for (i = 0; i < strlen(string); i++)
{
c = string[i];
if ((UINT8)c & 0x80) //color parsing! -Inuyasha 2.16.09
continue;
c = toupper(c) - HU_FONTSTART;
if (c < 0 || c >= HU_FONTSIZE || !fontv[HU_FONT].font[c])
w += spacewidth;
else
w += (charwidth ? charwidth : SHORT(fontv[HU_FONT].font[c]->width)/2);
}
return w;
}
//
// Find string width from tny_font chars
//
INT32 V_ThinStringWidth(const char *string, INT32 option)
{
INT32 c, w = 0;
INT32 spacewidth = 2, charwidth = 0;
boolean lowercase = (option & V_ALLOWLOWERCASE);
size_t i;
switch (option & V_SPACINGMASK)
{
case V_MONOSPACE:
spacewidth = 5;
/* FALLTHRU */
case V_OLDSPACING:
charwidth = 5;
break;
// Out of video flags, so we're reusing this for alternate charwidth instead
/*case V_6WIDTHSPACE:
spacewidth = 3;*/
default:
break;
}
for (i = 0; i < strlen(string); i++)
{
c = string[i];
if ((UINT8)c & 0x80) //color parsing! -Inuyasha 2.16.09
continue;
if (!lowercase || !fontv[TINY_FONT].font[c-HU_FONTSTART])
c = toupper(c);
c -= HU_FONTSTART;
if (c < 0 || c >= HU_FONTSIZE || !fontv[TINY_FONT].font[c])
w += spacewidth;
else
{
w += (charwidth ? charwidth
: ((option & V_6WIDTHSPACE && i < strlen(string)-1) ? max(1, SHORT(fontv[TINY_FONT].font[c]->width)-1) // Reuse this flag for the alternate bunched-up spacing
: SHORT(fontv[TINY_FONT].font[c]->width)));
}
}
return w;
}
//
// Find string width from tny_font chars, 0.5x scale
//
INT32 V_SmallThinStringWidth(const char *string, INT32 option)
{
INT32 w = V_ThinStringWidth(string, option)<<FRACBITS;
return w/2 + FRACUNIT; // +FRACUNIT because otherwise it's offset wrong.
}
boolean *heatshifter = NULL;
INT32 lastheight = 0;
INT32 heatindex[MAXSPLITSCREENPLAYERS] = {0, 0, 0, 0};
//
// V_DoPostProcessor
//
// Perform a particular image postprocessing function.
//
#include "p_local.h"
void V_DoPostProcessor(INT32 view, postimg_t type, INT32 param)
{
#if NUMSCREENS < 5
// do not enable image post processing for ARM, SH and MIPS CPUs
(void)view;
(void)type;
(void)param;
#else
INT32 yoffset, xoffset;
#ifdef HWRENDER
if (rendermode != render_soft)
return;
#endif
if (view < 0 || view > 3 || view > r_splitscreen)
return;
if ((view == 1 && r_splitscreen == 1) || view >= 2)
yoffset = viewheight;
else
yoffset = 0;
if ((view == 1 || view == 3) && r_splitscreen > 1)
xoffset = viewwidth;
else
xoffset = 0;
if (type == postimg_water)
{
UINT8 *tmpscr = screens[4];
UINT8 *srcscr = screens[0];
INT32 y;
angle_t disStart = (leveltime * 128) & FINEMASK; // in 0 to FINEANGLE
INT32 newpix;
INT32 sine;
//UINT8 *transme = transtables + ((tr_trans50-1)<<FF_TRANSSHIFT);
for (y = yoffset; y < yoffset+viewheight; y++)
{
sine = (FINESINE(disStart)*5)>>FRACBITS;
newpix = abs(sine);
if (sine < 0)
{
M_Memcpy(&tmpscr[(y*vid.width)+xoffset+newpix], &srcscr[(y*vid.width)+xoffset], viewwidth-newpix);
// Cleanup edge
while (newpix)
{
tmpscr[(y*vid.width)+xoffset+newpix] = srcscr[(y*vid.width)+xoffset];
newpix--;
}
}
else
{
M_Memcpy(&tmpscr[(y*vid.width)+xoffset+0], &srcscr[(y*vid.width)+xoffset+sine], viewwidth-newpix);
// Cleanup edge
while (newpix)
{
tmpscr[(y*vid.width)+xoffset+viewwidth-newpix] = srcscr[(y*vid.width)+xoffset+(viewwidth-1)];
newpix--;
}
}
/*
Unoptimized version
for (x = 0; x < vid.width*vid.bpp; x++)
{
newpix = (x + sine);
if (newpix < 0)
newpix = 0;
else if (newpix >= vid.width)
newpix = vid.width-1;
tmpscr[y*vid.width + x] = srcscr[y*vid.width+newpix]; // *(transme + (srcscr[y*vid.width+x]<<8) + srcscr[y*vid.width+newpix]);
}*/
disStart += 22;//the offset into the displacement map, increment each game loop
disStart &= FINEMASK; //clip it to FINEMASK
}
VID_BlitLinearScreen(tmpscr+vid.width*vid.bpp*yoffset+xoffset, screens[0]+vid.width*vid.bpp*yoffset+xoffset,
viewwidth*vid.bpp, viewheight, vid.width*vid.bpp, vid.width);
}
else if (type == postimg_motion) // Motion Blur!
{
UINT8 *tmpscr = screens[4];
UINT8 *srcscr = screens[0];
INT32 x, y;
// TODO: Add a postimg_param so that we can pick the translucency level...
UINT8 *transme = transtables + ((param-1)<<FF_TRANSSHIFT);
for (y = yoffset; y < yoffset+viewheight; y++)
{
for (x = xoffset; x < xoffset+viewwidth; x++)
{
tmpscr[y*vid.width + x]
= colormaps[*(transme + (srcscr [(y*vid.width)+x ] <<8) + (tmpscr[(y*vid.width)+x]))];
}
}
VID_BlitLinearScreen(tmpscr+vid.width*vid.bpp*yoffset+xoffset, screens[0]+vid.width*vid.bpp*yoffset+xoffset,
viewwidth*vid.bpp, viewheight, vid.width*vid.bpp, vid.width);
}
else if (type == postimg_flip) // Flip the screen upside-down
{
UINT8 *tmpscr = screens[4];
UINT8 *srcscr = screens[0];
INT32 y, y2;
for (y = yoffset, y2 = yoffset+viewheight - 1; y < yoffset+viewheight; y++, y2--)
M_Memcpy(&tmpscr[(y2*vid.width)+xoffset], &srcscr[(y*vid.width)+xoffset], viewwidth);
VID_BlitLinearScreen(tmpscr+vid.width*vid.bpp*yoffset+xoffset, screens[0]+vid.width*vid.bpp*yoffset+xoffset,
viewwidth*vid.bpp, viewheight, vid.width*vid.bpp, vid.width);
}
else if (type == postimg_heat) // Heat wave
{
UINT8 *tmpscr = screens[4];
UINT8 *srcscr = screens[0];
INT32 y;
// Make sure table is built
if (heatshifter == NULL || lastheight != viewheight)
{
if (heatshifter)
Z_Free(heatshifter);
heatshifter = Z_Calloc(viewheight * sizeof(boolean), PU_STATIC, NULL);
for (y = 0; y < viewheight; y++)
{
if (M_RandomChance(FRACUNIT/8)) // 12.5%
heatshifter[y] = true;
}
heatindex[0] = heatindex[1] = heatindex[2] = heatindex[3] = 0;
lastheight = viewheight;
}
for (y = yoffset; y < yoffset+viewheight; y++)
{
if (heatshifter[heatindex[view]++])
{
// Shift this row of pixels to the right by 2
tmpscr[(y*vid.width)+xoffset] = srcscr[(y*vid.width)+xoffset];
M_Memcpy(&tmpscr[(y*vid.width)+xoffset], &srcscr[(y*vid.width)+xoffset+vid.dupx], viewwidth-vid.dupx);
}
else
M_Memcpy(&tmpscr[(y*vid.width)+xoffset], &srcscr[(y*vid.width)+xoffset], viewwidth);
heatindex[view] %= viewheight;
}
heatindex[view]++;
heatindex[view] %= vid.height;
VID_BlitLinearScreen(tmpscr+vid.width*vid.bpp*yoffset+xoffset, screens[0]+vid.width*vid.bpp*yoffset+xoffset,
viewwidth*vid.bpp, viewheight, vid.width*vid.bpp, vid.width);
}
else if (type == postimg_mirror) // Flip the screen on the x axis
{
UINT8 *tmpscr = screens[4];
UINT8 *srcscr = screens[0];
INT32 y, x, x2;
for (y = yoffset; y < yoffset+viewheight; y++)
{
for (x = xoffset, x2 = xoffset+((viewwidth*vid.bpp)-1); x < xoffset+(viewwidth*vid.bpp); x++, x2--)
tmpscr[y*vid.width + x2] = srcscr[y*vid.width + x];
}
VID_BlitLinearScreen(tmpscr+vid.width*vid.bpp*yoffset+xoffset, screens[0]+vid.width*vid.bpp*yoffset+xoffset,
viewwidth*vid.bpp, viewheight, vid.width*vid.bpp, vid.width);
}
#endif
}
// Generates a color look-up table
// which has up to 64 colors at each channel
// (see the defines in v_video.h)
UINT8 colorlookup[CLUTSIZE][CLUTSIZE][CLUTSIZE];
void InitColorLUT(RGBA_t *palette)
{
UINT8 r, g, b;
static boolean clutinit = false;
static RGBA_t *lastpalette = NULL;
if ((!clutinit) || (lastpalette != palette))
{
for (r = 0; r < CLUTSIZE; r++)
for (g = 0; g < CLUTSIZE; g++)
for (b = 0; b < CLUTSIZE; b++)
colorlookup[r][g][b] = NearestPaletteColor(r << SHIFTCOLORBITS, g << SHIFTCOLORBITS, b << SHIFTCOLORBITS, palette);
clutinit = true;
lastpalette = palette;
}
}
// V_Init
// old software stuff, buffers are allocated at video mode setup
// here we set the screens[x] pointers accordingly
// WARNING: called at runtime (don't init cvar here)
void V_Init(void)
{
INT32 i;
UINT8 *base = vid.buffer;
const INT32 screensize = vid.rowbytes * vid.height;
LoadMapPalette();
for (i = 0; i < NUMSCREENS; i++)
screens[i] = NULL;
// start address of NUMSCREENS * width*height vidbuffers
if (base)
{
for (i = 0; i < NUMSCREENS; i++)
screens[i] = base + i*screensize;
}
if (vid.direct)
screens[0] = vid.direct;
#ifdef DEBUG
CONS_Debug(DBG_RENDER, "V_Init done:\n");
for (i = 0; i < NUMSCREENS; i++)
CONS_Debug(DBG_RENDER, " screens[%d] = %x\n", i, screens[i]);
#endif
}