Kart-Public/src/v_video.c
toaster e76c328129 Sorry, Sal! In my defense, you don't need to recompile from scratch for any of this, despite what it may look like, as the skincolour name lists are walled off from the rest of the dehacked lists...
More menu/highlight things:
* Rename "Default" to "Game type", to make clearer what it's doing.
* Tweak race colouring slightly.
* Put a highlight around the level select icon, and make it consistent with voting.
* Allow setting the highlight colour to affect the intermissions.
* Fix bug with gametype colour getting for record attack.

A handful of skincolour things:
* Rename Turqouise to Aqua, because it resembles one of my fav 2.2 colours with that name.
* Fix incomplete Steel Blue -> Steel rename.
* Merge the very similar Blueberry and Sapphire...
* ... so Sapphire can be backported!
* And Ruby too.
2018-07-02 13:20:04 +01:00

2315 lines
57 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-2016 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", CV_SAVE, 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};
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_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;
boolean flip = false;
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
// oh please
if (rendermode != render_soft && !con_startup)
{
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[hudtrans];
else if (alphalevel == 14)
alphalevel = 10 - hudtrans;
/*else if (alphalevel == 15)
alphalevel = hudplusalpha[hudtrans];*/
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);
if (scrn & V_OFFSET) // Crosshair shit
{
y -= FixedMul((SHORT(patch->topoffset)*dupy)<<FRACBITS, pscale);
x -= FixedMul((SHORT(patch->leftoffset)*dupx)<<FRACBITS, pscale);
}
else
{
y -= FixedMul(SHORT(patch->topoffset)<<FRACBITS, pscale);
if (scrn & V_FLIP)
{
flip = true;
x -= FixedMul((SHORT(patch->width) - SHORT(patch->leftoffset))<<FRACBITS, pscale);
}
else
x -= FixedMul(SHORT(patch->leftoffset)<<FRACBITS, pscale);
}
if (scrn & V_SPLITSCREEN)
y += (BASEVIDHEIGHT/2)<<FRACBITS;
if (scrn & V_HORZSCREEN)
x += (BASEVIDWIDTH/2)<<FRACBITS;
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 && FixedMul(SHORT(patch->width)<<FRACBITS, pscale)>>FRACBITS == BASEVIDWIDTH
&& y == 0 && FixedMul(SHORT(patch->height)<<FRACBITS, pscale)>>FRACBITS == 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_HORZSCREEN|V_SNAPTOLEFT)) == (V_HORZSCREEN|V_SNAPTOLEFT))
x += (vid.width/2 - (BASEVIDWIDTH/2 * dupx));
else 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_SNAPTOTOP)) == (V_SPLITSCREEN|V_SNAPTOTOP))
desttop += (vid.height/2 - (BASEVIDHEIGHT/2 * dupy)) * vid.width;
else if (scrn & V_SNAPTOBOTTOM)
desttop += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width;
else if (!(scrn & V_SNAPTOTOP))
desttop += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width / 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 (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 (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;
desttop += (y*vid.width) + x;
// Center it if necessary
if (!(scrn & V_SCALEPATCHMASK))
{
if (vid.width != BASEVIDWIDTH * dupx)
{
// dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
// so center this imaginary screen
if (scrn & V_SNAPTORIGHT)
desttop += (vid.width - (BASEVIDWIDTH * dupx));
else if (!(scrn & V_SNAPTOLEFT))
desttop += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
}
if (vid.height != BASEVIDHEIGHT * dupy)
{
// same thing here
if (scrn & V_SNAPTOBOTTOM)
desttop += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width;
else if (!(scrn & V_SNAPTOTOP))
desttop += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width / 2;
}
// 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]));
}
}
}
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 (skins[skinnum].flags & SF_HIRES
#ifdef HWRENDER
// || (rendermode != render_soft && rendermode != render_none)
#endif
)
V_DrawScaledPatch(x - 10, y - 14, flags, W_CachePatchName("CONTINS", PU_CACHE));
else
{
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], (UINT8)(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 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
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 (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--;
}
}
//
// 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...
// 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_soft && rendermode != render_none)
{
HWR_FadeScreenMenuBack(color, strength);
return;
}
#endif
{
const UINT8 *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.
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];
}
}
// 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
//
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 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;
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;
}
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;
}
}
// SRB2kart
void V_DrawKartString(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 = 12, 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;
}
charflags = (option & V_CHARCOLORMASK);
switch (option & V_SPACINGMASK)
{
case V_MONOSPACE:
spacewidth = 12;
/* FALLTHRU */
case V_OLDSPACING:
charwidth = 12;
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 -= KART_FONTSTART;
// character does not exist or is a space
if (c < 0 || c >= KART_FONTSIZE || !kart_font[c])
{
cx += spacewidth * dupx;
continue;
}
if (charwidth)
{
w = charwidth * dupx;
center = w/2 - SHORT(kart_font[c]->width)*dupx/2;
}
else
w = SHORT(kart_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, kart_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;
}
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;
}
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;
}
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+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;
}
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 += 12*dupx;
continue;
}
w = SHORT(lt_font[c]->width) * dupx;
if (cx > scrwidth)
break;
if (cx + left < 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 += 12;
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 yoffset, xoffset;
#ifdef HWRENDER
// draw a hardware converted patch
if (rendermode != render_soft && rendermode != render_none)
return;
#endif
if (view < 0 || view > 3 || view > splitscreen)
return;
if ((view == 1 && splitscreen == 1) || view >= 2)
yoffset = viewheight;
else
yoffset = 0;
if ((view == 1 || view == 3) && 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;
if (splitscreen > 1) // 3P/4P has trouble supporting this, anyone want to fix it? :p
return;
// 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] = 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
}
// 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
}