ref: d75ec8b2381cd6f8e61bb8fbbad8ea41246fd8ee
dir: /v_video.c/
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 1993-2008 Raven Software
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Gamma correction LUT stuff.
// Functions to draw patches (by post) directly to screen.
// Functions to blit a block to the screen.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "i_system.h"
#include "doomtype.h"
#include "deh_str.h"
#include "i_input.h"
#include "i_system.h"
#include "i_video.h"
#include "m_bbox.h"
#include "m_config.h"
#include "m_misc.h"
#include "v_video.h"
#include "w_wad.h"
#include "z_zone.h"
#include "config.h"
// Blending table used for fuzzpatch, etc.
// Only used in Heretic/Hexen
byte *tinttable = NULL;
// villsa [STRIFE] Blending table used for Strife
byte *xlatab = NULL;
// The screen buffer that the v_video.c code draws to.
static pixel_t *dest_screen = NULL;
int dirtybox[4];
// haleyjd 08/28/10: clipping callback function for patches.
// This is needed for Chocolate Strife, which clips patches to the screen.
static vpatchclipfunc_t patchclip_callback = NULL;
//
// V_MarkRect
//
void V_MarkRect(int x, int y, int width, int height)
{
// If we are temporarily using an alternate screen, do not
// affect the update box.
if (dest_screen == I_VideoBuffer)
{
M_AddToBox (dirtybox, x, y);
M_AddToBox (dirtybox, x + width-1, y + height-1);
}
}
//
// V_CopyRect
//
void V_CopyRect(int srcx, int srcy, pixel_t *source,
int width, int height,
int destx, int desty)
{
pixel_t *src;
pixel_t *dest;
//#ifdef RANGECHECK
if (srcx < 0
|| srcx + width > SCREENWIDTH
|| srcy < 0
|| srcy + height > SCREENHEIGHT
|| destx < 0
|| destx + width > SCREENWIDTH
|| desty < 0
|| desty + height > SCREENHEIGHT)
{
I_Error ("Bad V_CopyRect");
}
//#endif
V_MarkRect(destx, desty, width, height);
src = source + SCREENWIDTH * srcy + srcx;
dest = dest_screen + SCREENWIDTH * desty + destx;
for ( ; height>0 ; height--)
{
memcpy(dest, src, width * sizeof(*dest));
src += SCREENWIDTH;
dest += SCREENWIDTH;
}
}
//
// V_SetPatchClipCallback
//
// haleyjd 08/28/10: Added for Strife support.
// By calling this function, you can setup runtime error checking for patch
// clipping. Strife never caused errors by drawing patches partway off-screen.
// Some versions of vanilla DOOM also behaved differently than the default
// implementation, so this could possibly be extended to those as well for
// accurate emulation.
//
void V_SetPatchClipCallback(vpatchclipfunc_t func)
{
patchclip_callback = func;
}
//
// V_DrawPatch
// Masks a column based masked pic to the screen.
//
void V_DrawPatch(int x, int y, patch_t *patch)
{
int count;
int col;
column_t *column;
pixel_t *desttop;
pixel_t *dest;
byte *source;
int w;
y -= LE_SHORT(patch->topoffset);
x -= LE_SHORT(patch->leftoffset);
// haleyjd 08/28/10: Strife needs silent error checking here.
if(patchclip_callback)
{
if(!patchclip_callback(patch, x, y))
return;
}
//#ifdef RANGECHECK
if (x < 0
|| x + LE_SHORT(patch->width) > SCREENWIDTH
|| y < 0
|| y + LE_SHORT(patch->height) > SCREENHEIGHT)
{
I_Error("Bad V_DrawPatch");
}
//#endif
V_MarkRect(x, y, LE_SHORT(patch->width), LE_SHORT(patch->height));
col = 0;
desttop = dest_screen + y * SCREENWIDTH + x;
w = LE_SHORT(patch->width);
for ( ; col<w ; x++, col++, desttop++)
{
column = (column_t *)((byte *)patch + LE_LONG(patch->columnofs[col]));
// step through the posts in a column
while (column->topdelta != 0xff)
{
source = (byte *)column + 3;
dest = desttop + column->topdelta*SCREENWIDTH;
count = column->length;
while (count--)
{
*dest = *source++;
dest += SCREENWIDTH;
}
column = (column_t *)((byte *)column + column->length + 4);
}
}
}
//
// V_DrawPatchFlipped
// Masks a column based masked pic to the screen.
// Flips horizontally, e.g. to mirror face.
//
void V_DrawPatchFlipped(int x, int y, patch_t *patch)
{
int count;
int col;
column_t *column;
pixel_t *desttop;
pixel_t *dest;
byte *source;
int w;
y -= LE_SHORT(patch->topoffset);
x -= LE_SHORT(patch->leftoffset);
// haleyjd 08/28/10: Strife needs silent error checking here.
if(patchclip_callback)
{
if(!patchclip_callback(patch, x, y))
return;
}
//#ifdef RANGECHECK
if (x < 0
|| x + LE_SHORT(patch->width) > SCREENWIDTH
|| y < 0
|| y + LE_SHORT(patch->height) > SCREENHEIGHT)
{
I_Error("Bad V_DrawPatchFlipped");
}
//#endif
V_MarkRect (x, y, LE_SHORT(patch->width), LE_SHORT(patch->height));
col = 0;
desttop = dest_screen + y * SCREENWIDTH + x;
w = LE_SHORT(patch->width);
for ( ; col<w ; x++, col++, desttop++)
{
column = (column_t *)((byte *)patch + LE_LONG(patch->columnofs[w-1-col]));
// step through the posts in a column
while (column->topdelta != 0xff )
{
source = (byte *)column + 3;
dest = desttop + column->topdelta*SCREENWIDTH;
count = column->length;
while (count--)
{
*dest = *source++;
dest += SCREENWIDTH;
}
column = (column_t *)((byte *)column + column->length + 4);
}
}
}
//
// V_DrawPatchDirect
// Draws directly to the screen on the pc.
//
void V_DrawPatchDirect(int x, int y, patch_t *patch)
{
V_DrawPatch(x, y, patch);
}
//
// V_DrawTLPatch
//
// Masks a column based translucent masked pic to the screen.
//
void V_DrawTLPatch(int x, int y, patch_t * patch)
{
int count, col;
column_t *column;
pixel_t *desttop, *dest;
byte *source;
int w;
y -= LE_SHORT(patch->topoffset);
x -= LE_SHORT(patch->leftoffset);
if (x < 0
|| x + LE_SHORT(patch->width) > SCREENWIDTH
|| y < 0
|| y + LE_SHORT(patch->height) > SCREENHEIGHT)
{
I_Error("Bad V_DrawTLPatch");
}
col = 0;
desttop = dest_screen + y * SCREENWIDTH + x;
w = LE_SHORT(patch->width);
for (; col < w; x++, col++, desttop++)
{
column = (column_t *) ((byte *) patch + LE_LONG(patch->columnofs[col]));
// step through the posts in a column
while (column->topdelta != 0xff)
{
source = (byte *) column + 3;
dest = desttop + column->topdelta * SCREENWIDTH;
count = column->length;
while (count--)
{
*dest = tinttable[((*dest) << 8) + *source++];
dest += SCREENWIDTH;
}
column = (column_t *) ((byte *) column + column->length + 4);
}
}
}
//
// V_DrawXlaPatch
//
// villsa [STRIFE] Masks a column based translucent masked pic to the screen.
//
void V_DrawXlaPatch(int x, int y, patch_t * patch)
{
int count, col;
column_t *column;
pixel_t *desttop, *dest;
byte *source;
int w;
y -= LE_SHORT(patch->topoffset);
x -= LE_SHORT(patch->leftoffset);
if(patchclip_callback)
{
if(!patchclip_callback(patch, x, y))
return;
}
col = 0;
desttop = dest_screen + y * SCREENWIDTH + x;
w = LE_SHORT(patch->width);
for(; col < w; x++, col++, desttop++)
{
column = (column_t *) ((byte *) patch + LE_LONG(patch->columnofs[col]));
// step through the posts in a column
while(column->topdelta != 0xff)
{
source = (byte *) column + 3;
dest = desttop + column->topdelta * SCREENWIDTH;
count = column->length;
while(count--)
{
*dest = xlatab[*dest + ((*source) << 8)];
source++;
dest += SCREENWIDTH;
}
column = (column_t *) ((byte *) column + column->length + 4);
}
}
}
//
// V_DrawAltTLPatch
//
// Masks a column based translucent masked pic to the screen.
//
void V_DrawAltTLPatch(int x, int y, patch_t * patch)
{
int count, col;
column_t *column;
pixel_t *desttop, *dest;
byte *source;
int w;
y -= LE_SHORT(patch->topoffset);
x -= LE_SHORT(patch->leftoffset);
if (x < 0
|| x + LE_SHORT(patch->width) > SCREENWIDTH
|| y < 0
|| y + LE_SHORT(patch->height) > SCREENHEIGHT)
{
I_Error("Bad V_DrawAltTLPatch");
}
col = 0;
desttop = dest_screen + y * SCREENWIDTH + x;
w = LE_SHORT(patch->width);
for (; col < w; x++, col++, desttop++)
{
column = (column_t *) ((byte *) patch + LE_LONG(patch->columnofs[col]));
// step through the posts in a column
while (column->topdelta != 0xff)
{
source = (byte *) column + 3;
dest = desttop + column->topdelta * SCREENWIDTH;
count = column->length;
while (count--)
{
*dest = tinttable[((*dest) << 8) + *source++];
dest += SCREENWIDTH;
}
column = (column_t *) ((byte *) column + column->length + 4);
}
}
}
//
// V_DrawShadowedPatch
//
// Masks a column based masked pic to the screen.
//
void V_DrawShadowedPatch(int x, int y, patch_t *patch)
{
int count, col;
column_t *column;
pixel_t *desttop, *dest;
byte *source;
pixel_t *desttop2, *dest2;
int w;
y -= LE_SHORT(patch->topoffset);
x -= LE_SHORT(patch->leftoffset);
if (x < 0
|| x + LE_SHORT(patch->width) > SCREENWIDTH
|| y < 0
|| y + LE_SHORT(patch->height) > SCREENHEIGHT)
{
I_Error("Bad V_DrawShadowedPatch");
}
col = 0;
desttop = dest_screen + y * SCREENWIDTH + x;
desttop2 = dest_screen + (y + 2) * SCREENWIDTH + x + 2;
w = LE_SHORT(patch->width);
for (; col < w; x++, col++, desttop++, desttop2++)
{
column = (column_t *) ((byte *) patch + LE_LONG(patch->columnofs[col]));
// step through the posts in a column
while (column->topdelta != 0xff)
{
source = (byte *) column + 3;
dest = desttop + column->topdelta * SCREENWIDTH;
dest2 = desttop2 + column->topdelta * SCREENWIDTH;
count = column->length;
while (count--)
{
*dest2 = tinttable[((*dest2) << 8)];
dest2 += SCREENWIDTH;
*dest = *source++;
dest += SCREENWIDTH;
}
column = (column_t *) ((byte *) column + column->length + 4);
}
}
}
//
// Load tint table from TINTTAB lump.
//
void V_LoadTintTable(void)
{
tinttable = W_CacheLumpName("TINTTAB", PU_STATIC);
}
//
// V_LoadXlaTable
//
// villsa [STRIFE] Load xla table from XLATAB lump.
//
void V_LoadXlaTable(void)
{
xlatab = W_CacheLumpName("XLATAB", PU_STATIC);
}
//
// V_DrawBlock
// Draw a linear block of pixels into the view buffer.
//
void V_DrawBlock(int x, int y, int width, int height, pixel_t *src)
{
pixel_t *dest;
//#ifdef RANGECHECK
if (x < 0
|| x + width >SCREENWIDTH
|| y < 0
|| y + height > SCREENHEIGHT)
{
I_Error ("Bad V_DrawBlock");
}
//#endif
V_MarkRect (x, y, width, height);
dest = dest_screen + y * SCREENWIDTH + x;
while (height--)
{
memcpy (dest, src, width * sizeof(*dest));
src += width;
dest += SCREENWIDTH;
}
}
void V_DrawFilledBox(int x, int y, int w, int h, int c)
{
uint8_t *buf, *buf1;
int x1, y1;
buf = I_VideoBuffer + SCREENWIDTH * y + x;
for (y1 = 0; y1 < h; ++y1)
{
buf1 = buf;
for (x1 = 0; x1 < w; ++x1)
{
*buf1++ = c;
}
buf += SCREENWIDTH;
}
}
void V_DrawHorizLine(int x, int y, int w, int c)
{
uint8_t *buf;
int x1;
buf = I_VideoBuffer + SCREENWIDTH * y + x;
for (x1 = 0; x1 < w; ++x1)
{
*buf++ = c;
}
}
void V_DrawVertLine(int x, int y, int h, int c)
{
uint8_t *buf;
int y1;
buf = I_VideoBuffer + SCREENWIDTH * y + x;
for (y1 = 0; y1 < h; ++y1)
{
*buf = c;
buf += SCREENWIDTH;
}
}
void V_DrawBox(int x, int y, int w, int h, int c)
{
V_DrawHorizLine(x, y, w, c);
V_DrawHorizLine(x, y+h-1, w, c);
V_DrawVertLine(x, y, h, c);
V_DrawVertLine(x+w-1, y, h, c);
}
//
// Draw a "raw" screen (lump containing raw data to blit directly
// to the screen)
//
void V_DrawRawScreen(byte *raw)
{
memcpy(dest_screen, raw, SCREENWIDTH * SCREENHEIGHT);
}
//
// V_Init
//
void V_Init (void)
{
// no-op!
// There used to be separate screens that could be drawn to; these are
// now handled in the upper layers.
}
// Set the buffer that the code draws to.
void V_UseBuffer(pixel_t *buffer)
{
dest_screen = buffer;
}
// Restore screen buffer to the i_video screen buffer.
void V_RestoreBuffer(void)
{
dest_screen = I_VideoBuffer;
}
//
// V_ScreenShot
//
void V_ScreenShot(void)
{
byte *palette = W_CacheLumpName("PLAYPAL", PU_CACHE);
char *filename = M_StringJoin(configdir, "screenshot.png", NULL);
I_WritePNG(filename, I_VideoBuffer, SCREENWIDTH, SCREENHEIGHT, palette);
free(filename);
}