vkdoom_m/src/textures/formats/patchtexture.cpp
Christoph Oelckers 82bd742ea3 - reworked how the software renderer manages its textures.
* it's no longer the main texture objects managing the pixel buffer but FSoftwareTexture.
* create proper spans for true color textures. The paletted spans only match if the image does not have any translucent pixels.
* create proper warp textures instead of working off the paletted variants.

As a side effect, caching of pixel buffers for texture composition is temporarily disabled, as it management of texture redirections. These things will be reimplemented once things progress further. The existing methods here had their share of serious issues that should be fixed.
2018-12-08 12:42:35 +01:00

312 lines
9.2 KiB
C++

/*
** patchtexture.cpp
** Texture class for single Doom patches
**
**---------------------------------------------------------------------------
** Copyright 2004-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 "doomtype.h"
#include "files.h"
#include "w_wad.h"
#include "v_palette.h"
#include "v_video.h"
#include "bitmap.h"
// posts are runs of non masked source pixels
struct column_t
{
uint8_t topdelta; // -1 is the last post in a column
uint8_t length; // length data bytes follows
};
bool checkPatchForAlpha(const void *buffer, uint32_t length);
//==========================================================================
//
// A texture that is just a single Doom format patch
//
//==========================================================================
class FPatchTexture : public FWorldTexture
{
bool badflag = false;
bool isalpha = false;
public:
FPatchTexture (int lumpnum, patch_t *header, bool isalphatex);
TArray<uint8_t> Get8BitPixels(bool alphatex) override;
int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf) override;
void DetectBadPatches();
bool UseBasePalette() override { return !isalpha; }
FTextureFormat GetFormat() override { return isalpha ? TEX_RGB : TEX_Pal; } // should be TEX_Gray instead of TEX_RGB. Maybe later when all is working.
};
//==========================================================================
//
// Checks if the currently open lump can be a Doom patch
//
//==========================================================================
static bool CheckIfPatch(FileReader & file, bool &isalpha)
{
if (file.GetLength() < 13) return false; // minimum length of a valid Doom patch
uint8_t *data = new uint8_t[file.GetLength()];
file.Seek(0, FileReader::SeekSet);
file.Read(data, file.GetLength());
const patch_t *foo = (const patch_t *)data;
int height = LittleShort(foo->height);
int width = LittleShort(foo->width);
if (height > 0 && height <= 2048 && width > 0 && width <= 2048 && width < file.GetLength()/4)
{
// The dimensions seem like they might be valid for a patch, so
// check the column directory for extra security. At least one
// column must begin exactly at the end of the column directory,
// and none of them must point past the end of the patch.
bool gapAtStart = true;
int x;
for (x = 0; x < width; ++x)
{
uint32_t ofs = LittleLong(foo->columnofs[x]);
if (ofs == (uint32_t)width * 4 + 8)
{
gapAtStart = false;
}
else if (ofs >= (uint32_t)(file.GetLength())) // Need one byte for an empty column (but there's patches that don't know that!)
{
delete [] data;
return false;
}
}
if (!gapAtStart)
{
// only check this if the texture passed validation.
// Here is a good point because we already have a valid buffer of the lump's data.
isalpha = checkPatchForAlpha(data, (uint32_t)file.GetLength());
}
delete[] data;
return !gapAtStart;
}
delete [] data;
return false;
}
//==========================================================================
//
//
//
//==========================================================================
FTexture *PatchTexture_TryCreate(FileReader & file, int lumpnum)
{
patch_t header;
bool isalpha;
if (!CheckIfPatch(file, isalpha)) return NULL;
file.Seek(0, FileReader::SeekSet);
header.width = file.ReadUInt16();
header.height = file.ReadUInt16();
header.leftoffset = file.ReadInt16();
header.topoffset = file.ReadInt16();
return new FPatchTexture(lumpnum, &header, isalpha);
}
//==========================================================================
//
//
//
//==========================================================================
FPatchTexture::FPatchTexture (int lumpnum, patch_t * header, bool isalphatex)
: FWorldTexture(NULL, lumpnum)
{
isalpha = isalphatex;
Width = header->width;
Height = header->height;
_LeftOffset[1] = _LeftOffset[0] = header->leftoffset;
_TopOffset[1] = _TopOffset[0] = header->topoffset;
DetectBadPatches();
}
//==========================================================================
//
//
//
//==========================================================================
TArray<uint8_t> FPatchTexture::Get8BitPixels(bool alphatex)
{
uint8_t *remap, remaptable[256];
int numspans;
const column_t *maxcol;
int x;
FMemLump lump = Wads.ReadLump (SourceLump);
const patch_t *patch = (const patch_t *)lump.GetMem();
maxcol = (const column_t *)((const uint8_t *)patch + Wads.LumpLength (SourceLump) - 3);
remap = GetRemap(alphatex, isalpha);
// Special case for skies
if (bNoRemap0 && remap == GPalette.Remap)
{
memcpy(remaptable, GPalette.Remap, 256);
remaptable[0] = 0;
remap = remaptable;
}
if (badflag)
{
TArray<uint8_t> Pixels(Width * Height, true);
uint8_t *out;
// Draw the image to the buffer
for (x = 0, out = Pixels.Data(); x < Width; ++x)
{
const uint8_t *in = (const uint8_t *)patch + LittleLong(patch->columnofs[x]) + 3;
for (int y = Height; y > 0; --y)
{
*out = remap[*in];
out++, in++;
}
}
return Pixels;
}
int numpix = Width * Height;
numspans = Width;
TArray<uint8_t> Pixels(numpix, true);
memset (Pixels.Data(), 0, numpix);
// Draw the image to the buffer
for (x = 0; x < Width; ++x)
{
uint8_t *outtop = Pixels.Data() + x*Height;
const column_t *column = (const column_t *)((const uint8_t *)patch + LittleLong(patch->columnofs[x]));
int top = -1;
while (column < maxcol && column->topdelta != 0xFF)
{
if (column->topdelta <= top)
{
top += column->topdelta;
}
else
{
top = column->topdelta;
}
int len = column->length;
uint8_t *out = outtop + top;
if (len != 0)
{
if (top + len > Height) // Clip posts that extend past the bottom
{
len = Height - top;
}
if (len > 0)
{
numspans++;
const uint8_t *in = (const uint8_t *)column + 3;
for (int i = 0; i < len; ++i)
{
out[i] = remap[in[i]];
}
}
}
column = (const column_t *)((const uint8_t *)column + column->length + 4);
}
}
return Pixels;
}
//==========================================================================
//
//
//
//==========================================================================
int FPatchTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf)
{
if (!isalpha) return FTexture::CopyTrueColorPixels(bmp, x, y, rotate, inf);
else return CopyTrueColorTranslated(bmp, x, y, rotate, translationtables[TRANSLATION_Standard][STD_Grayscale]->Palette, inf);
}
//==========================================================================
//
// Fix for certain special patches on single-patch textures.
//
//==========================================================================
void FPatchTexture::DetectBadPatches ()
{
// The patch must look like it is large enough for the rules to apply to avoid using this on truly empty patches.
if (Wads.LumpLength(SourceLump) < Width * Height / 2) return;
// Check if this patch is likely to be a problem.
// It must be 256 pixels tall, and all its columns must have exactly
// one post, where each post has a supposed length of 0.
FMemLump lump = Wads.ReadLump (SourceLump);
const patch_t *realpatch = (patch_t *)lump.GetMem();
const uint32_t *cofs = realpatch->columnofs;
int x, x2 = LittleShort(realpatch->width);
if (LittleShort(realpatch->height) == 256)
{
for (x = 0; x < x2; ++x)
{
const column_t *col = (column_t*)((uint8_t*)realpatch+LittleLong(cofs[x]));
if (col->topdelta != 0 || col->length != 0)
{
return; // It's not bad!
}
col = (column_t *)((uint8_t *)col + 256 + 4);
if (col->topdelta != 0xFF)
{
return; // More than one post in a column!
}
}
_LeftOffset[1] = _LeftOffset[0] = 0;
_TopOffset[1] = _TopOffset[0] = 0;
badflag = true;
bMasked = false; // Hacked textures don't have transparent parts.
}
}