SRB2/src/v_video.c
2018-11-25 07:35:38 -05:00

2092 lines
52 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-2018 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 "v_video.h"
#include "hu_stuff.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
// 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
static CV_PossibleValue_t gamma_cons_t[] = {{0, "MIN"}, {4, "MAX"}, {0, NULL}};
static void CV_usegamma_OnChange(void);
consvar_t cv_ticrate = {"showfps", "No", 0, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_usegamma = {"gamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_usegamma_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_allcaps = {"allcaps", "Off", 0, CV_OnOff, NULL, 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};
#ifdef HWRENDER
static void CV_Gammaxxx_ONChange(void);
// Saved hardware mode variables
// - You can change them in software,
// but they won't do anything.
static CV_PossibleValue_t grgamma_cons_t[] = {{1, "MIN"}, {255, "MAX"}, {0, NULL}};
static CV_PossibleValue_t grsoftwarefog_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "LightPlanes"}, {0, NULL}};
consvar_t cv_voodoocompatibility = {"gr_voodoocompatibility", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_grfovchange = {"gr_fovchange", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_grfog = {"gr_fog", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_grfogcolor = {"gr_fogcolor", "AAAAAA", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_grsoftwarefog = {"gr_softwarefog", "Off", CV_SAVE, grsoftwarefog_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_grgammared = {"gr_gammared", "127", CV_SAVE|CV_CALL, grgamma_cons_t,
CV_Gammaxxx_ONChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_grgammagreen = {"gr_gammagreen", "127", CV_SAVE|CV_CALL, grgamma_cons_t,
CV_Gammaxxx_ONChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_grgammablue = {"gr_gammablue", "127", CV_SAVE|CV_CALL, grgamma_cons_t,
CV_Gammaxxx_ONChange, 0, NULL, NULL, 0, 0, NULL};
#ifdef ALAM_LIGHTING
consvar_t cv_grdynamiclighting = {"gr_dynamiclighting", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_grstaticlighting = {"gr_staticlighting", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_grcoronas = {"gr_coronas", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_grcoronasize = {"gr_coronasize", "1", CV_SAVE| CV_FLOAT, 0, NULL, 0, NULL, NULL, 0, 0, NULL};
#endif
static CV_PossibleValue_t CV_MD2[] = {{0, "Off"}, {1, "On"}, {2, "Old"}, {0, NULL}};
// console variables in development
consvar_t cv_grmd2 = {"gr_md2", "Off", CV_SAVE, CV_MD2, NULL, 0, NULL, NULL, 0, 0, NULL};
#endif
const UINT8 gammatable[5][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},
{2,4,5,7,8,10,11,12,14,15,16,18,19,20,21,23,24,25,26,27,29,30,31,
32,33,34,36,37,38,39,40,41,42,44,45,46,47,48,49,50,51,52,54,55,
56,57,58,59,60,61,62,63,64,65,66,67,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,129,129,
130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,
146,147,148,148,149,150,151,152,153,154,155,156,157,158,159,160,
161,162,163,163,164,165,166,167,168,169,170,171,172,173,174,175,
175,176,177,178,179,180,181,182,183,184,185,186,186,187,188,189,
190,191,192,193,194,195,196,196,197,198,199,200,201,202,203,204,
205,205,206,207,208,209,210,211,212,213,214,214,215,216,217,218,
219,220,221,222,222,223,224,225,226,227,228,229,230,230,231,232,
233,234,235,236,237,237,238,239,240,241,242,243,244,245,245,246,
247,248,249,250,251,252,252,253,254,255},
{4,7,9,11,13,15,17,19,21,22,24,26,27,29,30,32,33,35,36,38,39,40,42,
43,45,46,47,48,50,51,52,54,55,56,57,59,60,61,62,63,65,66,67,68,69,
70,72,73,74,75,76,77,78,79,80,82,83,84,85,86,87,88,89,90,91,92,93,
94,95,96,97,98,100,101,102,103,104,105,106,107,108,109,110,111,112,
113,114,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,
129,130,131,132,133,133,134,135,136,137,138,139,140,141,142,143,144,
144,145,146,147,148,149,150,151,152,153,153,154,155,156,157,158,159,
160,160,161,162,163,164,165,166,166,167,168,169,170,171,172,172,173,
174,175,176,177,178,178,179,180,181,182,183,183,184,185,186,187,188,
188,189,190,191,192,193,193,194,195,196,197,197,198,199,200,201,201,
202,203,204,205,206,206,207,208,209,210,210,211,212,213,213,214,215,
216,217,217,218,219,220,221,221,222,223,224,224,225,226,227,228,228,
229,230,231,231,232,233,234,235,235,236,237,238,238,239,240,241,241,
242,243,244,244,245,246,247,247,248,249,250,251,251,252,253,254,254,
255},
{8,12,16,19,22,24,27,29,31,34,36,38,40,41,43,45,47,49,50,52,53,55,
57,58,60,61,63,64,65,67,68,70,71,72,74,75,76,77,79,80,81,82,84,85,
86,87,88,90,91,92,93,94,95,96,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,129,130,131,132,133,134,135,135,136,137,138,139,140,
141,142,143,143,144,145,146,147,148,149,150,150,151,152,153,154,155,
155,156,157,158,159,160,160,161,162,163,164,165,165,166,167,168,169,
169,170,171,172,173,173,174,175,176,176,177,178,179,180,180,181,182,
183,183,184,185,186,186,187,188,189,189,190,191,192,192,193,194,195,
195,196,197,197,198,199,200,200,201,202,202,203,204,205,205,206,207,
207,208,209,210,210,211,212,212,213,214,214,215,216,216,217,218,219,
219,220,221,221,222,223,223,224,225,225,226,227,227,228,229,229,230,
231,231,232,233,233,234,235,235,236,237,237,238,238,239,240,240,241,
242,242,243,244,244,245,246,246,247,247,248,249,249,250,251,251,252,
253,253,254,254,255},
{16,23,28,32,36,39,42,45,48,50,53,55,57,60,62,64,66,68,69,71,73,75,76,
78,80,81,83,84,86,87,89,90,92,93,94,96,97,98,100,101,102,103,105,106,
107,108,109,110,112,113,114,115,116,117,118,119,120,121,122,123,124,
125,126,128,128,129,130,131,132,133,134,135,136,137,138,139,140,141,
142,143,143,144,145,146,147,148,149,150,150,151,152,153,154,155,155,
156,157,158,159,159,160,161,162,163,163,164,165,166,166,167,168,169,
169,170,171,172,172,173,174,175,175,176,177,177,178,179,180,180,181,
182,182,183,184,184,185,186,187,187,188,189,189,190,191,191,192,193,
193,194,195,195,196,196,197,198,198,199,200,200,201,202,202,203,203,
204,205,205,206,207,207,208,208,209,210,210,211,211,212,213,213,214,
214,215,216,216,217,217,218,219,219,220,220,221,221,222,223,223,224,
224,225,225,226,227,227,228,228,229,229,230,230,231,232,232,233,233,
234,234,235,235,236,236,237,237,238,239,239,240,240,241,241,242,242,
243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251,
251,252,252,253,254,254,255,255}
};
// local copy of the palette for V_GetColor()
RGBA_t *pLocalPalette = NULL;
// 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)
{
const UINT8 *usegamma = gammatable[cv_usegamma.value];
lumpnum_t lumpnum = W_GetNumForName(lumpname);
size_t i, palsize = W_LumpLength(lumpnum)/3;
UINT8 *pal;
Z_Free(pLocalPalette);
pLocalPalette = Z_Malloc(sizeof (*pLocalPalette)*palsize, PU_STATIC, NULL);
pal = W_CacheLumpNum(lumpnum, PU_CACHE);
for (i = 0; i < palsize; i++)
{
pLocalPalette[i].s.red = usegamma[*pal++];
pLocalPalette[i].s.green = usegamma[*pal++];
pLocalPalette[i].s.blue = usegamma[*pal++];
pLocalPalette[i].s.alpha = 0xFF;
}
}
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(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_soft && rendermode != render_none)
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_soft && rendermode != render_none)
HWR_SetPalette(pLocalPalette);
#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL)
else
#endif
#endif
if (rendermode != render_none)
I_SetPalette(pLocalPalette);
}
static void CV_usegamma_OnChange(void)
{
// reload palette
LoadMapPalette();
V_SetPalette(0);
}
// change the palette directly to see the change
#ifdef HWRENDER
static void CV_Gammaxxx_ONChange(void)
{
if (rendermode != render_soft && rendermode != render_none)
V_SetPalette(0);
}
#endif
#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)
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};
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_DrawFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, 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;
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_soft)
{
HWR_DrawFixedPatch((GLPatch_t *)patch, x, y, pscale, scrn, colormap);
return;
}
#endif
patchdrawfunc = standardpdraw;
v_translevel = NULL;
if ((alphalevel = ((scrn & V_ALPHAMASK) >> V_ALPHASHIFT)))
{
if (alphalevel == 13)
alphalevel = hudminusalpha[cv_translucenthud.value];
else if (alphalevel == 14)
alphalevel = 10 - cv_translucenthud.value;
else if (alphalevel == 15)
alphalevel = hudplusalpha[cv_translucenthud.value];
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 = FixedMul(dupx<<FRACBITS, pscale);
colfrac = FixedDiv(FRACUNIT, fdup);
rowfrac = FixedDiv(FRACUNIT, fdup);
// 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, pscale);
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;
}
if (scrn & V_SPLITSCREEN)
y>>=1;
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))
{
// if it's meant to cover the whole screen, black out the rest
if (x == 0 && SHORT(patch->width) == BASEVIDWIDTH && y == 0 && SHORT(patch->height) == BASEVIDHEIGHT)
{
column = (const column_t *)((const UINT8 *)(patch) + LONG(patch->columnofs[0]));
source = (const UINT8 *)(column) + 3;
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0]));
}
if (vid.width != BASEVIDWIDTH * dupx)
{
// dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
// so center this imaginary screen
if (scrn & V_SNAPTORIGHT)
x += (vid.width - (BASEVIDWIDTH * dupx));
else if (!(scrn & V_SNAPTOLEFT))
x += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
}
if (vid.height != BASEVIDHEIGHT * dupy)
{
// same thing here
if ((scrn & (V_SPLITSCREEN|V_SNAPTOBOTTOM)) == (V_SPLITSCREEN|V_SNAPTOBOTTOM))
y += (vid.height/2 - (BASEVIDHEIGHT/2 * dupy));
else if (scrn & V_SNAPTOBOTTOM)
y += (vid.height - (BASEVIDHEIGHT * dupy));
else if (!(scrn & V_SNAPTOTOP))
y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2;
}
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,fdup))*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)
{
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
// Done
if (rendermode != render_soft && !con_startup)
{
HWR_DrawCroppedPatch((GLPatch_t*)patch,x,y,pscale,scrn,sx,sy,w,h);
return;
}
#endif
// 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
if (!(scrn & V_SCALEPATCHMASK))
{
// if it's meant to cover the whole screen, black out the rest
if (x == 0 && SHORT(patch->width) == BASEVIDWIDTH && y == 0 && SHORT(patch->height) == BASEVIDHEIGHT)
{
column = (const column_t *)((const UINT8 *)(patch) + LONG(patch->columnofs[0]));
source = (const UINT8 *)(column) + 3;
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0]));
}
if (vid.width != BASEVIDWIDTH * dupx)
{
// dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
// so center this imaginary screen
if (scrn & V_SNAPTORIGHT)
x += (vid.width - (BASEVIDWIDTH * dupx));
else if (!(scrn & V_SNAPTOLEFT))
x += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
}
if (vid.height != BASEVIDHEIGHT * dupy)
{
// same thing here
if (scrn & V_SNAPTOBOTTOM)
y += (vid.height - (BASEVIDHEIGHT * dupy));
else if (!(scrn & V_SNAPTOTOP))
y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2;
}
}
desttop += (y*vid.width) + x;
}
for (col = sx<<FRACBITS; (col>>FRACBITS) < SHORT(patch->width) && (col>>FRACBITS) < 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;
dest += FixedInt(FixedMul(topdelta<<FRACBITS,fdup))*vid.width;
for (ofs = sy<<FRACBITS; dest < deststop && (ofs>>FRACBITS) < column->length && (ofs>>FRACBITS) < h; ofs += rowfrac)
{
if (dest >= screens[scrn&V_PARAMMASK]) // don't draw off the top of the screen (CRASH PREVENTION)
*dest = source[ofs>>FRACBITS];
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, UINT8 skincolor)
{
if (skinnum < 0 || skinnum >= numskins || (skins[skinnum].flags & SF_HIRES))
V_DrawScaledPatch(x - 10, y - 14, flags, W_CachePatchName("CONTINS", PU_CACHE)); // Draw a star
else
{ // Find front angle of the first waiting frame of the character's actual sprites
spriteframe_t *sprframe = &skins[skinnum].spritedef.spriteframes[2 & FF_FRAMEMASK];
patch_t *patch = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE);
const UINT8 *colormap = R_GetTranslationColormap(skinnum, skincolor, GTC_CACHE);
// No variant for translucency
V_DrawTinyMappedPatch(x, y, flags, patch, colormap);
}
}
//
// 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;
}
}
static void V_BlitScaledPic(INT32 px1, INT32 py1, INT32 scrn, pic_t *pic);
// Draw a linear pic, scaled, TOTALLY CRAP CODE!!! OPTIMISE AND ASM!!
//
void V_DrawScaledPic(INT32 rx1, INT32 ry1, INT32 scrn, INT32 lumpnum)
{
#ifdef HWRENDER
if (rendermode != render_soft)
{
HWR_DrawPic(rx1, ry1, lumpnum);
return;
}
#endif
V_BlitScaledPic(rx1, ry1, scrn, W_CacheLumpNum(lumpnum, PU_CACHE));
}
static void V_BlitScaledPic(INT32 rx1, INT32 ry1, INT32 scrn, pic_t * pic)
{
INT32 dupx, dupy;
INT32 x, y;
UINT8 *src, *dest;
INT32 width, height;
width = SHORT(pic->width);
height = SHORT(pic->height);
scrn &= V_PARAMMASK;
if (pic->mode != 0)
{
CONS_Debug(DBG_RENDER, "pic mode %d not supported in Software\n", pic->mode);
return;
}
dest = screens[scrn] + max(0, ry1 * vid.width) + max(0, rx1);
// y cliping to the screen
if (ry1 + height * vid.dupy >= vid.width)
height = (vid.width - ry1) / vid.dupy - 1;
// WARNING no x clipping (not needed for the moment)
for (y = max(0, -ry1 / vid.dupy); y < height; y++)
{
for (dupy = vid.dupy; dupy; dupy--)
{
src = pic->data + y * width;
for (x = 0; x < width; x++)
{
for (dupx = vid.dupx; dupx; dupx--)
*dest++ = *src;
src++;
}
dest += vid.width - vid.dupx * width;
}
}
}
//
// 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)
{
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
if (vid.width != BASEVIDWIDTH * dupx)
{
// dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
// so center this imaginary screen
if (c & V_SNAPTORIGHT)
x += (vid.width - (BASEVIDWIDTH * dupx));
else if (!(c & V_SNAPTOLEFT))
x += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
}
if (vid.height != BASEVIDHEIGHT * dupy)
{
// same thing here
if (c & V_SNAPTOBOTTOM)
y += (vid.height - (BASEVIDHEIGHT * dupy));
else if (!(c & V_SNAPTOTOP))
y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2;
}
}
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);
}
//
// 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_soft && rendermode != render_none)
{
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);
}
}
//
// Fade all the screen buffer, so that the menu is more readable,
// especially now that we use the small hufont in the menus...
//
void V_DrawFadeScreen(void)
{
const UINT8 *fadetable = (UINT8 *)colormaps + 16*256;
const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
UINT8 *buf = screens[0];
#ifdef HWRENDER
if (rendermode != render_soft && rendermode != render_none)
{
HWR_FadeScreenMenuBack(0x01010160, 0); // hack, 0 means full height
return;
}
#endif
// 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];
}
// 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_soft && rendermode != render_none)
{
UINT32 hwcolor;
switch (cons_backcolor.value)
{
case 0: hwcolor = 0xffffff00; break; // White
case 1: hwcolor = 0x80808000; break; // Gray
case 2: hwcolor = 0x40201000; break; // Brown
case 3: hwcolor = 0xff000000; break; // Red
case 4: hwcolor = 0xff800000; break; // Orange
case 5: hwcolor = 0x80800000; break; // Yellow
case 6: hwcolor = 0x00800000; break; // Green
case 7: hwcolor = 0x0000ff00; break; // Blue
case 8: hwcolor = 0x4080ff00; break; // Cyan
// Default green
default: hwcolor = 0x00800000; break;
}
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];
}
// Gets string colormap, used for 0x80 color codes
//
static const UINT8 *V_GetStringColormap(INT32 colorflags)
{
switch ((colorflags & V_CHARCOLORMASK) >> V_CHARCOLORSHIFT)
{
case 1: // 0x81, purple
return purplemap;
case 2: // 0x82, yellow
return yellowmap;
case 3: // 0x83, lgreen
return lgreenmap;
case 4: // 0x84, blue
return bluemap;
case 5: // 0x85, red
return redmap;
case 6: // 0x86, gray
return graymap;
case 7: // 0x87, orange
return orangemap;
default: // reset
return NULL;
}
}
// 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 || !hu_font[c])
return;
w = SHORT(hu_font[c]->width);
if (x + w > vid.width)
return;
if (colormap != NULL)
V_DrawMappedPatch(x, y, flags, hu_font[c], colormap);
else
V_DrawScaledPatch(x, y, flags, hu_font[c]);
}
// 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 && (UINT8)c <= 0x89) //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 || !hu_font[c])
{
chw = spacewidth;
lastusablespace = i;
}
else
chw = (charwidth ? charwidth : hu_font[c]->width);
x += chw;
if (lastusablespace != 0 && x > w)
{
newstring[lastusablespace] = '\n';
i = lastusablespace;
lastusablespace = 0;
x = 0;
}
}
return newstring;
}
//
// Write a string using the hu_font
// NOTE: the text is centered for screens larger than the base width
//
void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string)
{
INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, center = 0, left = 0;
const char *ch = string;
INT32 charflags = 0;
const UINT8 *colormap = NULL;
INT32 spacewidth = 4, charwidth = 0;
INT32 lowercase = (option & V_ALLOWLOWERCASE);
option &= ~V_FLIP; // which is also shared with V_ALLOWLOWERCASE...
if (option & V_NOSCALESTART)
{
dupx = vid.dupx;
dupy = vid.dupy;
scrwidth = vid.width;
}
else
{
dupx = dupy = 1;
scrwidth = vid.width/vid.dupx;
left = (scrwidth - BASEVIDWIDTH)/2;
scrwidth -= left;
}
charflags = (option & V_CHARCOLORMASK);
switch (option & V_SPACINGMASK)
{
case V_MONOSPACE:
spacewidth = 8;
/* FALLTHRU */
case V_OLDSPACING:
charwidth = 8;
break;
case V_6WIDTHSPACE:
spacewidth = 6;
default:
break;
}
for (;;ch++)
{
if (!*ch)
break;
if (*ch & 0x80) //color parsing -x 2.16.09
{
// manually set flags override color codes
if (!(option & V_CHARCOLORMASK))
charflags = ((*ch & 0x7f) << V_CHARCOLORSHIFT) & V_CHARCOLORMASK;
continue;
}
if (*ch == '\n')
{
cx = x;
if (option & V_RETURN8)
cy += 8*dupy;
else
cy += 12*dupy;
continue;
}
c = *ch;
if (!lowercase)
c = toupper(c);
c -= HU_FONTSTART;
// character does not exist or is a space
if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
{
cx += spacewidth * dupx;
continue;
}
if (charwidth)
{
w = charwidth * dupx;
center = w/2 - SHORT(hu_font[c]->width)*dupx/2;
}
else
w = SHORT(hu_font[c]->width) * dupx;
if (cx > scrwidth)
break;
if (cx+left + w < 0) //left boundary check
{
cx += w;
continue;
}
colormap = V_GetStringColormap(charflags);
V_DrawFixedPatch((cx + center)<<FRACBITS, cy<<FRACBITS, FRACUNIT, option, hu_font[c], colormap);
cx += w;
}
}
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);
}
//
// Write a string using the hu_font, 0.5x scale
// NOTE: the text is centered for screens larger than the base width
//
void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string)
{
INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, center = 0, left = 0;
const char *ch = string;
INT32 charflags = 0;
const UINT8 *colormap = NULL;
INT32 spacewidth = 2, charwidth = 0;
INT32 lowercase = (option & V_ALLOWLOWERCASE);
option &= ~V_FLIP; // which is also shared with V_ALLOWLOWERCASE...
if (option & V_NOSCALESTART)
{
dupx = vid.dupx;
dupy = vid.dupy;
scrwidth = vid.width;
}
else
{
dupx = dupy = 1;
scrwidth = vid.width/vid.dupx;
left = (scrwidth - BASEVIDWIDTH)/2;
scrwidth -= left;
}
charflags = (option & V_CHARCOLORMASK);
switch (option & V_SPACINGMASK)
{
case V_MONOSPACE:
spacewidth = 4;
/* FALLTHRU */
case V_OLDSPACING:
charwidth = 4;
break;
case V_6WIDTHSPACE:
spacewidth = 3;
default:
break;
}
for (;;ch++)
{
if (!*ch)
break;
if (*ch & 0x80) //color parsing -x 2.16.09
{
// manually set flags override color codes
if (!(option & V_CHARCOLORMASK))
charflags = ((*ch & 0x7f) << V_CHARCOLORSHIFT) & V_CHARCOLORMASK;
continue;
}
if (*ch == '\n')
{
cx = x;
if (option & V_RETURN8)
cy += 4*dupy;
else
cy += 6*dupy;
continue;
}
c = *ch;
if (!lowercase)
c = toupper(c);
c -= HU_FONTSTART;
if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
{
cx += spacewidth * dupx;
continue;
}
if (charwidth)
{
w = charwidth * dupx;
center = w/2 - SHORT(hu_font[c]->width)*dupx/4;
}
else
w = SHORT(hu_font[c]->width) * dupx / 2;
if (cx > scrwidth)
break;
if (cx+left + w < 0) //left boundary check
{
cx += w;
continue;
}
colormap = V_GetStringColormap(charflags);
V_DrawFixedPatch((cx + center)<<FRACBITS, cy<<FRACBITS, FRACUNIT/2, option, hu_font[c], colormap);
cx += w;
}
}
void V_DrawRightAlignedSmallString(INT32 x, INT32 y, INT32 option, const char *string)
{
x -= V_SmallStringWidth(string, option);
V_DrawSmallString(x, y, option, string);
}
//
// Write a string using the tny_font
// NOTE: the text is centered for screens larger than the base width
//
void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string)
{
INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, left = 0;
const char *ch = string;
INT32 charflags = 0;
const UINT8 *colormap = NULL;
INT32 spacewidth = 2, charwidth = 0;
INT32 lowercase = (option & V_ALLOWLOWERCASE);
option &= ~V_FLIP; // which is also shared with V_ALLOWLOWERCASE...
if (option & V_NOSCALESTART)
{
dupx = vid.dupx;
dupy = vid.dupy;
scrwidth = vid.width;
}
else
{
dupx = dupy = 1;
scrwidth = vid.width/vid.dupx;
left = (scrwidth - BASEVIDWIDTH)/2;
scrwidth -= left;
}
charflags = (option & V_CHARCOLORMASK);
switch (option & V_SPACINGMASK)
{
case V_MONOSPACE:
spacewidth = 5;
/* FALLTHRU */
case V_OLDSPACING:
charwidth = 5;
break;
case V_6WIDTHSPACE:
spacewidth = 3;
default:
break;
}
for (;;ch++)
{
if (!*ch)
break;
if (*ch & 0x80) //color parsing -x 2.16.09
{
// manually set flags override color codes
if (!(option & V_CHARCOLORMASK))
charflags = ((*ch & 0x7f) << V_CHARCOLORSHIFT) & V_CHARCOLORMASK;
continue;
}
if (*ch == '\n')
{
cx = x;
if (option & V_RETURN8)
cy += 8*dupy;
else
cy += 12*dupy;
continue;
}
c = *ch;
if (!lowercase || !tny_font[c-HU_FONTSTART])
c = toupper(c);
c -= HU_FONTSTART;
if (c < 0 || c >= HU_FONTSIZE || !tny_font[c])
{
cx += spacewidth * dupx;
continue;
}
if (charwidth)
w = charwidth * dupx;
else
w = (SHORT(tny_font[c]->width) * dupx);
if (cx > scrwidth)
break;
if (cx+left + w < 0) //left boundary check
{
cx += w;
continue;
}
colormap = V_GetStringColormap(charflags);
V_DrawFixedPatch(cx<<FRACBITS, cy<<FRACBITS, FRACUNIT, option, tny_font[c], colormap);
cx += w;
}
}
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 string at a fixed_t location.
void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
{
fixed_t cx = x, cy = y;
INT32 w, c, dupx, dupy, scrwidth, center = 0, left = 0;
const char *ch = string;
INT32 spacewidth = 4, charwidth = 0;
INT32 lowercase = (option & V_ALLOWLOWERCASE);
option &= ~V_FLIP; // which is also shared with V_ALLOWLOWERCASE...
if (option & V_NOSCALESTART)
{
dupx = vid.dupx;
dupy = vid.dupy;
scrwidth = vid.width;
}
else
{
dupx = dupy = 1;
scrwidth = vid.width/vid.dupx;
left = (scrwidth - BASEVIDWIDTH)/2;
scrwidth -= left;
}
switch (option & V_SPACINGMASK)
{
case V_MONOSPACE:
spacewidth = 8;
/* FALLTHRU */
case V_OLDSPACING:
charwidth = 8;
break;
case V_6WIDTHSPACE:
spacewidth = 6;
default:
break;
}
for (;;ch++)
{
if (!*ch)
break;
if (*ch & 0x80) //color ignoring
continue;
if (*ch == '\n')
{
cx = x;
if (option & V_RETURN8)
cy += (8*dupy)<<FRACBITS;
else
cy += (12*dupy)<<FRACBITS;
continue;
}
c = *ch;
if (!lowercase)
c = toupper(c);
c -= HU_FONTSTART;
// character does not exist or is a space
if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
{
cx += (spacewidth * dupx)<<FRACBITS;
continue;
}
if (charwidth)
{
w = charwidth * dupx;
center = w/2 - SHORT(hu_font[c]->width)*(dupx/2);
}
else
w = SHORT(hu_font[c]->width) * dupx;
if ((cx>>FRACBITS) > scrwidth)
break;
if ((cx>>FRACBITS)+left + w < 0) //left boundary check
{
cx += w<<FRACBITS;
continue;
}
V_DrawSciencePatch(cx + (center<<FRACBITS), cy, option, hu_font[c], FRACUNIT);
cx += w<<FRACBITS;
}
}
// 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(tallnum[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, tallnum[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(tallnum[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, tallnum[num % 10]);
num /= 10;
} while (--digits);
}
// Write a string using the credit font
// NOTE: the text is centered for screens larger than the base width
//
void V_DrawCreditString(fixed_t x, fixed_t y, INT32 option, const char *string)
{
INT32 w, c, dupx, dupy, scrwidth = BASEVIDWIDTH;
fixed_t cx = x, cy = y;
const char *ch = string;
// It's possible for string to be a null pointer
if (!string)
return;
if (option & V_NOSCALESTART)
{
dupx = vid.dupx;
dupy = vid.dupy;
scrwidth = vid.width;
}
else
dupx = dupy = 1;
for (;;)
{
c = *ch++;
if (!c)
break;
if (c == '\n')
{
cx = x;
cy += (12*dupy)<<FRACBITS;
continue;
}
c = toupper(c) - CRED_FONTSTART;
if (c < 0 || c >= CRED_FONTSIZE)
{
cx += (16*dupx)<<FRACBITS;
continue;
}
w = SHORT(cred_font[c]->width) * dupx;
if ((cx>>FRACBITS) > scrwidth)
break;
V_DrawSciencePatch(cx, cy, option, cred_font[c], FRACUNIT);
cx += w<<FRACBITS;
}
}
// 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(cred_font[c]->width);
}
return w;
}
// Write a string using the level title font
// NOTE: the text is centered for screens larger than the base width
//
void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string)
{
INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, left = 0;
const char *ch = string;
if (option & V_NOSCALESTART)
{
dupx = vid.dupx;
dupy = vid.dupy;
scrwidth = vid.width;
}
else
{
dupx = dupy = 1;
scrwidth = vid.width/vid.dupx;
left = (scrwidth - BASEVIDWIDTH)/2;
scrwidth -= left;
}
for (;;)
{
c = *ch++;
if (!c)
break;
if (c == '\n')
{
cx = x;
cy += 12*dupy;
continue;
}
c = toupper(c) - LT_FONTSTART;
if (c < 0 || c >= LT_FONTSIZE || !lt_font[c])
{
cx += 16*dupx;
continue;
}
w = SHORT(lt_font[c]->width) * dupx;
if (cx > scrwidth)
break;
if (cx+left + w < 0) //left boundary check
{
cx += w;
continue;
}
V_DrawScaledPatch(cx, cy, option, lt_font[c]);
cx += w;
}
}
// 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++)
{
c = toupper(string[i]) - LT_FONTSTART;
if (c < 0 || c >= LT_FONTSIZE || !lt_font[c])
w += 16;
else
w += SHORT(lt_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 = toupper(string[i]) - LT_FONTSTART;
if (c < 0 || c >= LT_FONTSIZE || !lt_font[c])
continue;
if (SHORT(lt_font[c]->height) > w)
w = SHORT(lt_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 && (UINT8)c <= 0x89) //color parsing! -Inuyasha 2.16.09
continue;
c = toupper(c) - HU_FONTSTART;
if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
w += spacewidth;
else
w += (charwidth ? charwidth : SHORT(hu_font[c]->width));
}
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 && (UINT8)c <= 0x89) //color parsing! -Inuyasha 2.16.09
continue;
c = toupper(c) - HU_FONTSTART;
if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
w += spacewidth;
else
w += (charwidth ? charwidth : SHORT(hu_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;
size_t i;
switch (option & V_SPACINGMASK)
{
case V_MONOSPACE:
spacewidth = 5;
/* FALLTHRU */
case V_OLDSPACING:
charwidth = 5;
break;
case V_6WIDTHSPACE:
spacewidth = 3;
default:
break;
}
for (i = 0; i < strlen(string); i++)
{
c = string[i];
if ((UINT8)c >= 0x80 && (UINT8)c <= 0x89) //color parsing! -Inuyasha 2.16.09
continue;
c = toupper(c) - HU_FONTSTART;
if (c < 0 || c >= HU_FONTSIZE || !tny_font[c])
w += spacewidth;
else
w += (charwidth ? charwidth : SHORT(tny_font[c]->width));
}
return w;
}
boolean *heatshifter = NULL;
INT32 lastheight = 0;
INT32 heatindex[2] = { 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 height, yoffset;
#ifdef HWRENDER
// draw a hardware converted patch
if (rendermode != render_soft && rendermode != render_none)
return;
#endif
if (view < 0 || view >= 2 || (view == 1 && !splitscreen))
return;
if (splitscreen)
height = vid.height/2;
else
height = vid.height;
if (view == 1)
yoffset = vid.height/2;
else
yoffset = 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+height; y++)
{
sine = (FINESINE(disStart)*5)>>FRACBITS;
newpix = abs(sine);
if (sine < 0)
{
M_Memcpy(&tmpscr[y*vid.width+newpix], &srcscr[y*vid.width], vid.width-newpix);
// Cleanup edge
while (newpix)
{
tmpscr[y*vid.width+newpix] = srcscr[y*vid.width];
newpix--;
}
}
else
{
M_Memcpy(&tmpscr[y*vid.width+0], &srcscr[y*vid.width+sine], vid.width-newpix);
// Cleanup edge
while (newpix)
{
tmpscr[y*vid.width+vid.width-newpix] = srcscr[y*vid.width+(vid.width-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, screens[0]+vid.width*vid.bpp*yoffset,
vid.width*vid.bpp, height, 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+height; y++)
{
for (x = 0; x < vid.width; 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, screens[0]+vid.width*vid.bpp*yoffset,
vid.width*vid.bpp, height, 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+height - 1; y < yoffset+height; y++, y2--)
M_Memcpy(&tmpscr[y2*vid.width], &srcscr[y*vid.width], vid.width);
VID_BlitLinearScreen(tmpscr+vid.width*vid.bpp*yoffset, screens[0]+vid.width*vid.bpp*yoffset,
vid.width*vid.bpp, height, 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 != height)
{
if (heatshifter)
Z_Free(heatshifter);
heatshifter = Z_Calloc(height * sizeof(boolean), PU_STATIC, NULL);
for (y = 0; y < height; y++)
{
if (M_RandomChance(FRACUNIT/8)) // 12.5%
heatshifter[y] = true;
}
heatindex[0] = heatindex[1] = 0;
lastheight = height;
}
for (y = yoffset; y < yoffset+height; y++)
{
if (heatshifter[heatindex[view]++])
{
// Shift this row of pixels to the right by 2
tmpscr[y*vid.width] = srcscr[y*vid.width];
M_Memcpy(&tmpscr[y*vid.width+vid.dupx], &srcscr[y*vid.width], vid.width-vid.dupx);
}
else
M_Memcpy(&tmpscr[y*vid.width], &srcscr[y*vid.width], vid.width);
heatindex[view] %= height;
}
heatindex[view]++;
heatindex[view] %= vid.height;
VID_BlitLinearScreen(tmpscr+vid.width*vid.bpp*yoffset, screens[0]+vid.width*vid.bpp*yoffset,
vid.width*vid.bpp, height, vid.width*vid.bpp, vid.width);
}
#endif
}
// 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();
// hardware modes do not use screens[] pointers
for (i = 0; i < NUMSCREENS; i++)
screens[i] = NULL;
if (rendermode != render_soft)
{
return; // be sure to cause a NULL read/write error so we detect it, in case of..
}
// 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+1; i++)
CONS_Debug(DBG_RENDER, " screens[%d] = %x\n", i, screens[i]);
#endif
}