qzdoom-gpl/src/v_draw.cpp
Randy Heit ef1a5a115f - Added a new MapMarker actor. Instead of appearing in the 3D view, it appears
on the automap instead. If its first argument is zero, the map marker itself
  appears on the automap, otherwise it is drawn on top of any actor with a TID
  matching that argument. If the second argument is one, then the map marker
  will only be appear if the player has previously seen the sector it is one.
  You can use Thing_Activate and Thing_Deactivate on markers to turn them on
  and off. And if you subclass MapMarker with DECORATE, you can easily make
  your own custom markers.
- Fixed: Map markers could not be drawn partially off the map. They were
  drawn either fully or not at all.
- Fixed: Map markers appeared in the wrong place on a rotated overlay map if
  screenblocks < 10.


SVN r356 (trunk)
2006-10-20 04:04:04 +00:00

678 lines
15 KiB
C++

/*
** v_draw.cpp
** Draw patches and blocks to a canvas
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include <stdio.h>
#include <stdarg.h>
#include "doomtype.h"
#include "v_video.h"
#include "m_swap.h"
#include "r_defs.h"
#include "r_draw.h"
#include "r_things.h"
#include "i_system.h"
#include "i_video.h"
#include "templates.h"
// [RH] Stretch values to make a 320x200 image best fit the screen
// without using fractional steppings
int CleanXfac, CleanYfac;
// [RH] Effective screen sizes that the above scale values give you
int CleanWidth, CleanHeight;
CVAR (Bool, hud_scale, false, CVAR_ARCHIVE);
void STACK_ARGS DCanvas::DrawTexture (FTexture *img, int x0, int y0, DWORD tags_first, ...)
{
FTexture::Span unmaskedSpan[2];
const FTexture::Span **spanptr, *spans;
static BYTE identitymap[256];
static short bottomclipper[MAXWIDTH], topclipper[MAXWIDTH];
va_list tags;
DWORD tag;
INTBOOL boolval;
int intval;
if (img == NULL || img->UseType == FTexture::TEX_Null)
{
return;
}
int texwidth = img->GetScaledWidth();
int texheight = img->GetScaledHeight();
int windowleft = 0;
int windowright = texwidth;
int dclip = this->GetHeight();
int uclip = 0;
int lclip = 0;
int rclip = this->GetWidth();
int destwidth = windowright << FRACBITS;
int destheight = texheight << FRACBITS;
int top = img->GetScaledTopOffset();
int left = img->GetScaledLeftOffset();
fixed_t alpha = FRACUNIT;
int fillcolor = -1;
const BYTE *translation = NULL;
INTBOOL alphaChannel = false;
INTBOOL flipX = false;
fixed_t shadowAlpha = 0;
int shadowColor = 0;
int virtWidth = this->GetWidth();
int virtHeight = this->GetHeight();
INTBOOL keepratio = false;
ERenderStyle style = STYLE_Count;
x0 <<= FRACBITS;
y0 <<= FRACBITS;
spanptr = &spans;
// Parse the tag list for attributes
va_start (tags, tags_first);
tag = tags_first;
while (tag != TAG_DONE)
{
va_list *more_p;
DWORD data;
switch (tag)
{
case TAG_IGNORE:
default:
data = va_arg (tags, DWORD);
break;
case TAG_MORE:
more_p = va_arg (tags, va_list *);
va_end (tags);
#ifdef __GNUC__
__va_copy (tags, *more_p);
#else
tags = *more_p;
#endif
break;
case DTA_DestWidth:
destwidth = va_arg (tags, int) << FRACBITS;
break;
case DTA_DestHeight:
destheight = va_arg (tags, int) << FRACBITS;
break;
case DTA_Clean:
boolval = va_arg (tags, INTBOOL);
if (boolval)
{
x0 = (x0 - 160*FRACUNIT) * CleanXfac + (Width * (FRACUNIT/2));
y0 = (y0 - 100*FRACUNIT) * CleanYfac + (Height * (FRACUNIT/2));
destwidth = texwidth * CleanXfac * FRACUNIT;
destheight = texheight * CleanYfac * FRACUNIT;
}
break;
case DTA_CleanNoMove:
boolval = va_arg (tags, INTBOOL);
if (boolval)
{
destwidth = texwidth * CleanXfac * FRACUNIT;
destheight = texheight * CleanYfac * FRACUNIT;
}
break;
case DTA_320x200:
boolval = va_arg (tags, INTBOOL);
if (boolval)
{
virtWidth = 320;
virtHeight = 200;
}
break;
case DTA_HUDRules:
{
bool xright = x0 < 0;
bool ybot = y0 < 0;
intval = va_arg (tags, int);
if (hud_scale)
{
x0 *= CleanXfac;
if (intval == HUD_HorizCenter)
x0 += Width * FRACUNIT / 2;
else if (xright)
x0 = Width * FRACUNIT + x0;
y0 *= CleanYfac;
if (ybot)
y0 = Height * FRACUNIT + y0;
destwidth = texwidth * CleanXfac * FRACUNIT;
destheight = texheight * CleanYfac * FRACUNIT;
}
else
{
if (intval == HUD_HorizCenter)
x0 += Width * FRACUNIT / 2;
else if (xright)
x0 = Width * FRACUNIT + x0;
if (ybot)
y0 = Height * FRACUNIT + y0;
}
}
break;
case DTA_VirtualWidth:
virtWidth = va_arg (tags, int);
break;
case DTA_VirtualHeight:
virtHeight = va_arg (tags, int);
break;
case DTA_Alpha:
alpha = MIN<fixed_t> (FRACUNIT, va_arg (tags, fixed_t));
break;
case DTA_AlphaChannel:
alphaChannel = va_arg (tags, INTBOOL);
break;
case DTA_FillColor:
fillcolor = va_arg (tags, int);
break;
case DTA_Translation:
translation = va_arg (tags, const BYTE *);
break;
case DTA_FlipX:
flipX = va_arg (tags, INTBOOL);
break;
case DTA_TopOffset:
top = va_arg (tags, int);
break;
case DTA_LeftOffset:
left = va_arg (tags, int);
break;
case DTA_CenterOffset:
if (va_arg (tags, int))
{
left = texwidth / 2;
top = texheight / 2;
}
break;
case DTA_CenterBottomOffset:
if (va_arg (tags, int))
{
left = texwidth / 2;
top = texheight;
}
break;
case DTA_WindowLeft:
windowleft = va_arg (tags, int);
break;
case DTA_WindowRight:
windowright = va_arg (tags, int);
break;
case DTA_ClipTop:
uclip = va_arg (tags, int);
if (uclip < 0)
{
uclip = 0;
}
break;
case DTA_ClipBottom:
dclip = va_arg (tags, int);
if (dclip > this->GetHeight())
{
dclip = this->GetHeight();
}
break;
case DTA_ClipLeft:
lclip = va_arg (tags, int);
if (lclip < 0)
{
lclip = 0;
}
break;
case DTA_ClipRight:
rclip = va_arg (tags, int);
if (rclip > this->GetWidth())
{
rclip = this->GetWidth();
}
break;
case DTA_ShadowAlpha:
shadowAlpha = MIN<fixed_t> (FRACUNIT, va_arg (tags, fixed_t));
break;
case DTA_ShadowColor:
shadowColor = va_arg (tags, int);
break;
case DTA_Shadow:
boolval = va_arg (tags, INTBOOL);
if (boolval)
{
shadowAlpha = FRACUNIT/2;
shadowColor = 0;
}
else
{
shadowAlpha = 0;
}
break;
case DTA_Masked:
boolval = va_arg (tags, INTBOOL);
if (boolval)
{
spanptr = &spans;
}
else
{
spanptr = NULL;
}
break;
case DTA_KeepRatio:
keepratio = va_arg (tags, INTBOOL);
break;
case DTA_RenderStyle:
style = ERenderStyle(va_arg (tags, int));
break;
}
tag = va_arg (tags, DWORD);
}
va_end (tags);
if (virtWidth != Width || virtHeight != Height)
{
int myratio = CheckRatio (Width, Height);
if (myratio != 0 && !keepratio)
{ // The target surface is not 4:3, so expand the specified
// virtual size to avoid undesired stretching of the image.
// Does not handle non-4:3 virtual sizes. I'll worry about
// those if somebody expresses a desire to use them.
x0 = Scale (Width*960, x0-virtWidth*FRACUNIT/2, virtWidth*BaseRatioSizes[myratio][0]) + Width*FRACUNIT/2;
y0 = Scale (Height, y0, virtHeight);
destwidth = FixedDiv (Width*960 * (destwidth>>FRACBITS), virtWidth*BaseRatioSizes[myratio][0]);
destheight = FixedDiv (Height * (destheight>>FRACBITS), virtHeight);
}
else
{
x0 = Scale (Width, x0, virtWidth);
y0 = Scale (Height, y0, virtHeight);
destwidth = FixedDiv (Width * (destwidth>>FRACBITS), virtWidth);
destheight = FixedDiv (Height * (destheight>>FRACBITS), virtHeight);
}
}
if (destwidth <= 0 || destheight <= 0)
{
return;
}
if (style == STYLE_Count)
{
if (fillcolor != -1)
{
if (alphaChannel)
{
style = STYLE_Shaded;
}
else if (alpha < FRACUNIT)
{
style = STYLE_TranslucentStencil;
}
else
{
style = STYLE_Stencil;
}
}
else if (alpha < FRACUNIT)
{
style = STYLE_Translucent;
}
else
{
style = STYLE_Normal;
}
}
fixedcolormap = identitymap;
ESPSResult mode = R_SetPatchStyle (style, alpha, 0, fillcolor);
if (style != STYLE_Shaded)
{
if (translation != NULL)
{
dc_colormap = (lighttable_t *)translation;
}
else
{
dc_colormap = identitymap;
}
}
BYTE *destorgsave = dc_destorg;
dc_destorg = screen->GetBuffer();
x0 -= Scale (left, destwidth, texwidth);
y0 -= Scale (top, destheight, texheight);
if (mode != DontDraw)
{
const BYTE *pixels;
int stop4;
if (spanptr == NULL)
{ // Create a single span for forced unmasked images
spans = unmaskedSpan;
unmaskedSpan[0].TopOffset = 0;
unmaskedSpan[0].Length = img->GetHeight();
unmaskedSpan[1].TopOffset = 0;
unmaskedSpan[1].Length = 0;
}
fixed_t centeryback = centeryfrac;
centeryfrac = 0;
sprtopscreen = y0;
spryscale = destheight / img->GetHeight();
// Fix precision errors that are noticeable at some resolutions
if ((spryscale * img->GetHeight()) >> FRACBITS != destheight >> FRACBITS)
spryscale++;
sprflipvert = false;
dc_iscale = 0xffffffffu / (unsigned)spryscale;
dc_texturemid = FixedMul (-y0, dc_iscale);
fixed_t frac = 0;
fixed_t xiscale = DivScale32 (img->GetWidth(), destwidth);
int x2 = (x0 + destwidth) >> FRACBITS;
if (bottomclipper[0] != dclip)
{
clearbufshort (bottomclipper, screen->GetWidth(), (short)dclip);
if (identitymap[1] != 1)
{
for (int i = 0; i < 256; ++i)
{
identitymap[i] = i;
}
}
}
if (uclip != 0)
{
if (topclipper[0] != uclip)
{
clearbufshort (topclipper, screen->GetWidth(), (short)uclip);
}
mceilingclip = topclipper;
}
else
{
mceilingclip = zeroarray;
}
mfloorclip = bottomclipper;
if (flipX)
{
frac = (img->GetWidth() << FRACBITS) - 1;
xiscale = -xiscale;
}
dc_x = x0 >> FRACBITS;
if (windowleft > 0 || windowright < texwidth)
{
fixed_t xscale = destwidth / texwidth;
dc_x += (windowleft * xscale) >> FRACBITS;
frac += windowleft << FRACBITS;
x2 -= ((texwidth - windowright) * xscale) >> FRACBITS;
}
if (dc_x < lclip)
{
frac += (lclip - dc_x) * xiscale;
dc_x = lclip;
}
if (x2 > rclip)
{
x2 = rclip;
}
if (destheight < 32*FRACUNIT)
{
mode = DoDraw0;
}
if (mode == DoDraw0)
{
// One column at a time
stop4 = dc_x;
}
else // DoDraw1
{
// Up to four columns at a time
stop4 = x2 & ~3;
}
if (dc_x < x2)
{
while ((dc_x < stop4) && (dc_x & 3))
{
pixels = img->GetColumn (frac >> FRACBITS, spanptr);
R_DrawMaskedColumn (pixels, spans);
dc_x++;
frac += xiscale;
}
while (dc_x < stop4)
{
rt_initcols();
for (int zz = 4; zz; --zz)
{
pixels = img->GetColumn (frac >> FRACBITS, spanptr);
R_DrawMaskedColumnHoriz (pixels, spans);
dc_x++;
frac += xiscale;
}
rt_draw4cols (dc_x - 4);
}
while (dc_x < x2)
{
pixels = img->GetColumn (frac >> FRACBITS, spanptr);
R_DrawMaskedColumn (pixels, spans);
dc_x++;
frac += xiscale;
}
}
centeryfrac = centeryback;
}
R_FinishSetPatchStyle ();
dc_destorg = destorgsave;
if (ticdup != 0 && menuactive == MENU_Off)
{
NetUpdate ();
}
}
void DCanvas::FillBorder (FTexture *img)
{
int myratio = CheckRatio (Width, Height);
if (myratio == 0)
{ // This is a 4:3 display, so no border to show
return;
}
int bordtop, bordbottom, bordleft, bordright, bord;
if (myratio & 4)
{ // Screen is taller than it is wide
bordleft = bordright = 0;
bord = Height - Height * BaseRatioSizes[myratio][3] / 48;
bordtop = bord / 2;
bordbottom = bord - bordtop;
}
else
{ // Screen is wider than it is tall
bordtop = bordbottom = 0;
bord = Width - Width * BaseRatioSizes[myratio][3] / 48;
bordleft = bord / 2;
bordright = bord - bordleft;
}
if (img != NULL)
{
FlatFill (0, 0, Width, bordtop, img); // Top
FlatFill (0, bordtop, bordleft, Height - bordbottom, img); // Left
FlatFill (Width - bordright, bordtop, Width, Height - bordbottom, img); // Right
FlatFill (0, Height - bordbottom, Width, Height, img); // Bottom
}
else
{
Clear (0, 0, Width, bordtop, 0); // Top
Clear (0, bordtop, bordleft, Height - bordbottom, 0); // Left
Clear (Width - bordright, bordtop, Width, Height - bordbottom, 0); // Right
Clear (0, Height - bordbottom, Width, Height, 0); // Bottom
}
}
/********************************/
/* */
/* Other miscellaneous routines */
/* */
/********************************/
//
// V_DrawBlock
// Draw a linear block of pixels into the view buffer.
//
void DCanvas::DrawBlock (int x, int y, int _width, int _height, const BYTE *src) const
{
int srcpitch = _width;
int destpitch;
BYTE *dest;
if (ClipBox (x, y, _width, _height, src, srcpitch))
{
return; // Nothing to draw
}
destpitch = Pitch;
dest = Buffer + y*Pitch + x;
do
{
memcpy (dest, src, _width);
src += srcpitch;
dest += destpitch;
} while (--_height);
}
//
// V_GetBlock
// Gets a linear block of pixels from the view buffer.
//
void DCanvas::GetBlock (int x, int y, int _width, int _height, BYTE *dest) const
{
const BYTE *src;
#ifdef RANGECHECK
if (x<0
||x+_width > Width
|| y<0
|| y+_height>Height)
{
I_Error ("Bad V_GetBlock");
}
#endif
src = Buffer + y*Pitch + x;
while (_height--)
{
memcpy (dest, src, _width);
src += Pitch;
dest += _width;
}
}
// Returns true if the box was completely clipped. False otherwise.
bool DCanvas::ClipBox (int &x, int &y, int &w, int &h, const BYTE *&src, const int srcpitch) const
{
if (x >= Width || y >= Height || x+w <= 0 || y+h <= 0)
{ // Completely clipped off screen
return true;
}
if (x < 0) // clip left edge
{
src -= x;
w += x;
x = 0;
}
if (x+w > Width) // clip right edge
{
w = Width - x;
}
if (y < 0) // clip top edge
{
src -= y*srcpitch;
h += y;
y = 0;
}
if (y+h > Height) // clip bottom edge
{
h = Height - y;
}
return false;
}