zduke/source/doomlayer/v_video.cpp
Rachael Alexanderson 129a83b4e4 - import
2020-07-17 04:59:23 -04:00

631 lines
12 KiB
C++

// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id:$
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This source is available for distribution and/or modification
// only under the terms of the DOOM Source Code License as
// published by id Software. All rights reserved.
//
// The source is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
// for more details.
//
// $Log:$
//
// DESCRIPTION:
// Functions to draw patches (by post) directly to screen->
// Functions to blit a block to the screen->
//
//-----------------------------------------------------------------------------
#include <stdio.h>
#include <math.h>
#include "m_alloc.h"
#include "duke3d.h"
#include "i_system.h"
#include "i_video.h"
//#include "r_local.h"
//#include "r_draw.h"
//#include "r_plane.h"
//#include "r_state.h"
#include "doomdef.h"
//#include "doomdata.h"
//#include "doomstat.h"
#include "c_console.h"
//#include "hu_stuff.h"
#include "m_argv.h"
//#include "m_bbox.h"
//#include "m_swap.h"
//#include "m_menu.h"
#include "i_video.h"
#include "v_video.h"
//#include "v_text.h"
//#include "w_wad.h"
#include "c_cvars.h"
#include "c_dispatch.h"
#include "cmdlib.h"
//#include "gi.h"
#include "templates.h"
int CleanWidth, CleanHeight, CleanXfac, CleanYfac;
int DisplayWidth, DisplayHeight, DisplayBits;
DFrameBuffer *screen;
CVAR (Int, vid_defwidth, 320, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Int, vid_defheight, 200, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR (Bool, vid_fps, false, 0)
CVAR (Bool, ticker, false, 0)
CVAR (Float, dimamount, 0.2f, CVAR_ARCHIVE)
CVAR (Color, dimcolor, 0xffd700, CVAR_ARCHIVE)
// [RH] Set true when vid_setmode command has been executed
bool setmodeneeded = false;
// [RH] Resolution to change to when setmodeneeded is true
int NewWidth, NewHeight, NewBits;
DCanvas *DCanvas::CanvasChain = NULL;
DCanvas::DCanvas (int _width, int _height)
{
// Init member vars
Buffer = NULL;
LockCount = 0;
Width = _width;
Height = _height;
// Add to list of active canvases
Next = CanvasChain;
CanvasChain = this;
}
DCanvas::~DCanvas ()
{
// Remove from list of active canvases
DCanvas *probe = CanvasChain, **prev;
prev = &CanvasChain;
probe = CanvasChain;
while (probe != NULL)
{
if (probe == this)
{
*prev = probe->Next;
break;
}
prev = &probe->Next;
probe = probe->Next;
}
}
bool DCanvas::IsValid ()
{
// A nun-subclassed DCanvas is never valid
return false;
}
// [RH] Set an area to a specified color
void DCanvas::Clear (int left, int top, int right, int bottom, int color) const
{
int x, y;
byte *dest;
dest = Buffer + top * Pitch + left;
x = right - left;
for (y = top; y < bottom; y++)
{
memset (dest, color, x);
dest += Pitch;
}
}
void DCanvas::Blit (int srcx, int srcy, int srcwidth, int srcheight,
DCanvas *dest, int destx, int desty, int destwidth, int destheight)
{
fixed_t fracxstep, fracystep;
fixed_t fracx, fracy;
int x, y;
bool lockthis, lockdest;
if ( (lockthis = (LockCount == 0)) )
{
if (Lock ())
{ // Surface was lost, so nothing to blit
Unlock ();
return;
}
}
if ( (lockdest = (dest->LockCount == 0)) )
{
dest->Lock ();
}
fracy = srcy << FRACBITS;
fracystep = (srcheight << FRACBITS) / destheight;
fracxstep = (srcwidth << FRACBITS) / destwidth;
byte *destline, *srcline;
byte *destbuffer = dest->Buffer;
byte *srcbuffer = Buffer;
if (fracxstep == FRACUNIT)
{
for (y = desty; y < desty + destheight; y++, fracy += fracystep)
{
memcpy (destbuffer + y * dest->Pitch + destx,
srcbuffer + (fracy >> FRACBITS) * Pitch + srcx,
destwidth);
}
}
else
{
for (y = desty; y < desty + destheight; y++, fracy += fracystep)
{
srcline = srcbuffer + (fracy >> FRACBITS) * Pitch + srcx;
destline = destbuffer + y * dest->Pitch + destx;
for (x = fracx = 0; x < destwidth; x++, fracx += fracxstep)
{
destline[x] = srcline[fracx >> FRACBITS];
}
}
}
if (lockthis)
{
Unlock ();
}
if (lockdest)
{
Unlock ();
}
}
void DCanvas::CalcGamma (float gamma, BYTE gammalookup[256])
{
// I found this formula on the web at
// <http://panda.mostang.com/sane/sane-gamma.html>
double invgamma = 1.f / gamma;
int i;
for (i = 0; i < 256; i++)
{
gammalookup[i] = (BYTE)(255.0 * pow (i / 255.0, invgamma));
}
}
DSimpleCanvas::DSimpleCanvas (int width, int height)
: DCanvas (width, height)
{
// Making the pitch a power of 2 is very bad for performance
if (width == 512 || width == 1024 || width == 2048)
{
Pitch = width + 8;
}
else
{
Pitch = width;
}
MemBuffer = new BYTE[Pitch * height];
}
DSimpleCanvas::~DSimpleCanvas ()
{
if (MemBuffer != NULL)
{
delete[] MemBuffer;
MemBuffer = NULL;
}
}
bool DSimpleCanvas::IsValid ()
{
return (MemBuffer != NULL);
}
bool DSimpleCanvas::Lock ()
{
if (LockCount == 0)
{
LockCount++;
Buffer = MemBuffer;
}
return false; // System surfaces are never lost
}
void DSimpleCanvas::Unlock ()
{
if (LockCount <= 1)
{
LockCount = 0;
Buffer = NULL; // Enforce buffer access only between Lock/Unlock
}
}
DFrameBuffer::DFrameBuffer (int width, int height)
: DSimpleCanvas (width, height)
{
LastMS = LastSec = FrameCount = LastCount = LastTic = 0;
}
void DFrameBuffer::DrawRateStuff ()
{
#if WORKINPROGRESS
// Draws frame time and cumulative fps
if (vid_fps)
{
QWORD ms = I_MSTime ();
QWORD howlong = ms - LastMS;
if (howlong > 0)
{
char fpsbuff[40];
int chars;
#if _MSC_VER
chars = sprintf (fpsbuff, "%I64d ms (%ld fps)", howlong, LastCount);
#else
chars = sprintf (fpsbuff, "%Ld ms (%ld fps)", howlong, LastCount);
#endif
Clear (0, screen->GetHeight() - 8, chars * 8, screen->GetHeight(), 0);
SetFont (ConFont);
DrawText (CR_WHITE, 0, screen->GetHeight() - 8, (char *)&fpsbuff[0]);
SetFont (SmallFont);
DWORD thisSec = ms/1000;
if (LastSec < thisSec)
{
LastCount = FrameCount / (thisSec - LastSec);
LastSec = thisSec;
FrameCount = 0;
}
FrameCount++;
}
LastMS = ms;
}
#endif
}
void DFrameBuffer::CopyFromBuff (BYTE *src, int srcPitch, int width, int height, BYTE *dest)
{
if (Pitch == width && Pitch == Width && srcPitch == width)
{
memcpy (dest, src, Width * Height);
}
else
{
for (int y = 0; y < height; y++)
{
memcpy (dest, src, width);
dest += Pitch;
src += srcPitch;
}
}
}
//
// V_SetResolution
//
bool V_DoModeSetup (int width, int height, int bits)
{
DFrameBuffer *buff = I_SetMode (width, height, screen);
if (buff == NULL)
{
return false;
}
screen = buff;
screen->SetGamma (Gamma);
CleanXfac = width / 320;
CleanYfac = height / 200;
if (CleanXfac > 1 && CleanYfac > 1 && CleanXfac != CleanYfac)
{
if (CleanXfac < CleanYfac)
CleanYfac = CleanXfac;
else
CleanXfac = CleanYfac;
}
CleanWidth = width / CleanXfac;
CleanHeight = height / CleanYfac;
DisplayWidth = width;
DisplayHeight = height;
DisplayBits = bits;
#if WORKINPROGRESS
M_RefreshModesList ();
#endif
return true;
}
bool V_SetResolution (int width, int height, int bits)
{
int oldwidth, oldheight;
int oldbits;
if (screen)
{
oldwidth = SCREENWIDTH;
oldheight = SCREENHEIGHT;
oldbits = DisplayBits;
}
else
{ // Harmless if screen wasn't allocated
oldwidth = width;
oldheight = height;
oldbits = bits;
}
I_ClosestResolution (&width, &height, bits);
if (!I_CheckResolution (width, height, bits))
{ // Try specified resolution
if (!I_CheckResolution (oldwidth, oldheight, oldbits))
{ // Try previous resolution (if any)
return false;
}
else
{
width = oldwidth;
height = oldheight;
bits = oldbits;
}
}
return V_DoModeSetup (width, height, bits);
}
CCMD (vid_setmode)
{
BOOL goodmode = false;
int width = 0, height = SCREENHEIGHT;
int bits = DisplayBits;
if (argv.argc() > 1)
{
width = atoi (argv[1]);
if (argv.argc() > 2)
{
height = atoi (argv[2]);
if (argv.argc() > 3)
{
bits = atoi (argv[3]);
}
}
}
if (width && I_CheckResolution (width, height, bits))
{
goodmode = true;
}
if (goodmode)
{
// The actual change of resolution will take place
// near the beginning of D_Display().
#if WORKINPROGRESS
if (gamestate != GS_STARTUP)
#endif
{
setmodeneeded = true;
NewWidth = width;
NewHeight = height;
NewBits = bits;
}
}
else if (width)
{
Printf ("Unknown resolution %d x %d x %d\n", width, height, bits);
}
else
{
Printf ("Usage: vid_setmode <width> <height> <mode>\n");
}
}
//
// V_Init
//
void V_Init (void)
{
char *i;
int width, height, bits;
width = height = bits = 0;
if ( (i = Args.CheckValue ("-width")) )
width = atoi (i);
if ( (i = Args.CheckValue ("-height")) )
height = atoi (i);
if (width == 0)
{
if (height == 0)
{
width = vid_defwidth;
height = vid_defheight;
}
else
{
width = (height * 8) / 6;
}
}
else if (height == 0)
{
height = (width * 6) / 8;
}
bits = 8;
atterm (FreeCanvasChain);
I_ClosestResolution (&width, &height, bits);
if (!V_SetResolution (width, height, bits))
I_FatalError ("Could not set resolution to %d x %d x %d", width, height, bits);
else
Printf ("Resolution: %d x %d\n", SCREENWIDTH, SCREENHEIGHT);
FBaseCVar::ResetColors ();
#if WORKINPROGRESS
ConFont = new FSingleLumpFont ("ConsoleFont", W_GetNumForName ("CONFONT"));
#endif
C_InitConsole (SCREENWIDTH, SCREENHEIGHT, true);
}
void STACK_ARGS FreeCanvasChain ()
{
while (DCanvas::CanvasChain != NULL)
{
delete DCanvas::CanvasChain;
}
screen = NULL;
}
//
// Break long lines of text into multiple lines no longer than maxwidth pixels
//
static void breakit (brokenlines_t *line, const byte *start, const byte *string, bool keepspace)
{
// Leave out trailing white space
if (!keepspace)
{
while (string > start && isspace (*(string - 1)))
string--;
}
line->string = new char[string - start + 1];
strncpy (line->string, (char *)start, string - start);
line->string[string - start] = 0;
line->width = gametextwidth (line->string, -1);
}
brokenlines_t *V_BreakLines (int maxwidth, const byte *string, bool keepspace)
{
brokenlines_t lines[128]; // Support up to 128 lines (should be plenty)
const byte *space = NULL, *start = string;
int i, c, w, nw;
BOOL lastWasSpace = false;
i = w = 0;
while ( (c = *string++) )
{
if (c == TEXTCOLOR_ESCAPE)
{
if (*string)
string++;
continue;
}
if (isspace(c))
{
if (!lastWasSpace)
{
space = string - 1;
lastWasSpace = true;
}
}
else
{
lastWasSpace = false;
}
nw = gamecharwidth (c);
if ((w > 0 && w + nw > maxwidth) || c == '\n')
{ // Time to break the line
if (!space)
space = string - 1;
lines[i].nlterminated = (c == '\n');
breakit (&lines[i], start, space, keepspace);
i++;
w = 0;
lastWasSpace = false;
start = space;
space = NULL;
while (*start && isspace (*start) && *start != '\n')
start++;
if (*start == '\n')
start++;
else
while (*start && isspace (*start))
start++;
string = start;
}
else
{
w += nw;
}
}
if (string - start > 1)
{
const byte *s = start;
while (s < string)
{
if (keepspace || !isspace (*s))
{
lines[i].nlterminated = (*s == '\n');
s++;
breakit (&lines[i++], start, string, keepspace);
break;
}
s++;
}
}
{
// Make a copy of the broken lines and return them
brokenlines_t *broken = new brokenlines_t[i+1];
memcpy (broken, lines, sizeof(brokenlines_t) * i);
broken[i].string = NULL;
broken[i].width = -1;
return broken;
}
}
void V_FreeBrokenLines (brokenlines_t *lines)
{
if (lines)
{
int i = 0;
while (lines[i].width != -1)
{
delete[] lines[i].string;
lines[i].string = NULL;
i++;
}
delete[] lines;
}
}