this is just to have it cleaner and less code bloat the resizing loop will almost never trigger, 1meg is enough to even rotate gigantamax eevee without needing to grow
806 lines
20 KiB
C++
806 lines
20 KiB
C++
// SONIC ROBO BLAST 2
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 1993-1996 by id Software, Inc.
|
|
// Copyright (C) 2005-2009 by Andrey "entryway" Budko.
|
|
// Copyright (C) 2018-2020 by Jaime "Lactozilla" Passos.
|
|
// Copyright (C) 2019-2020 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 r_patch.c
|
|
/// \brief Patch generation.
|
|
|
|
#include "byteptr.h"
|
|
#include "doomdef.h"
|
|
#include "r_patch.h"
|
|
#include "r_things.h"
|
|
#include "r_skins.h"
|
|
#include "z_zone.h"
|
|
#include "w_wad.h"
|
|
#include <vector>
|
|
|
|
#ifdef HWRENDER
|
|
#include "hardware/hw_glob.h"
|
|
#endif
|
|
|
|
//
|
|
// R_ParseSpriteInfoFrame
|
|
//
|
|
// Parse a SPRTINFO frame.
|
|
//
|
|
static void R_ParseSpriteInfoFrame(spriteinfo_t *info)
|
|
{
|
|
char *sprinfoToken;
|
|
size_t sprinfoTokenLength;
|
|
char *frameChar = NULL;
|
|
UINT8 frameFrame = 0xFF;
|
|
INT16 frameXPivot = 0;
|
|
INT16 frameYPivot = 0;
|
|
rotaxis_t frameRotAxis = ROTAXIS_X;
|
|
|
|
// Sprite identifier
|
|
sprinfoToken = M_GetToken(NULL);
|
|
if (sprinfoToken == NULL)
|
|
{
|
|
I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite frame should be");
|
|
}
|
|
sprinfoTokenLength = strlen(sprinfoToken);
|
|
if (sprinfoTokenLength != 1)
|
|
{
|
|
I_Error("Error parsing SPRTINFO lump: Invalid frame \"%s\"",sprinfoToken);
|
|
}
|
|
else
|
|
frameChar = sprinfoToken;
|
|
|
|
frameFrame = R_Char2Frame(frameChar[0]);
|
|
Z_Free(sprinfoToken);
|
|
|
|
// Left Curly Brace
|
|
sprinfoToken = M_GetToken(NULL);
|
|
if (sprinfoToken == NULL)
|
|
I_Error("Error parsing SPRTINFO lump: Missing sprite info");
|
|
else
|
|
{
|
|
if (strcmp(sprinfoToken,"{")==0)
|
|
{
|
|
Z_Free(sprinfoToken);
|
|
sprinfoToken = M_GetToken(NULL);
|
|
if (sprinfoToken == NULL)
|
|
{
|
|
I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite info should be");
|
|
}
|
|
while (strcmp(sprinfoToken,"}")!=0)
|
|
{
|
|
if (stricmp(sprinfoToken, "XPIVOT")==0)
|
|
{
|
|
Z_Free(sprinfoToken);
|
|
sprinfoToken = M_GetToken(NULL);
|
|
frameXPivot = atoi(sprinfoToken);
|
|
}
|
|
else if (stricmp(sprinfoToken, "YPIVOT")==0)
|
|
{
|
|
Z_Free(sprinfoToken);
|
|
sprinfoToken = M_GetToken(NULL);
|
|
frameYPivot = atoi(sprinfoToken);
|
|
}
|
|
else if (stricmp(sprinfoToken, "ROTAXIS")==0)
|
|
{
|
|
Z_Free(sprinfoToken);
|
|
sprinfoToken = M_GetToken(NULL);
|
|
if ((stricmp(sprinfoToken, "X")==0) || (stricmp(sprinfoToken, "XAXIS")==0) || (stricmp(sprinfoToken, "ROLL")==0))
|
|
frameRotAxis = ROTAXIS_X;
|
|
else if ((stricmp(sprinfoToken, "Y")==0) || (stricmp(sprinfoToken, "YAXIS")==0) || (stricmp(sprinfoToken, "PITCH")==0))
|
|
frameRotAxis = ROTAXIS_Y;
|
|
else if ((stricmp(sprinfoToken, "Z")==0) || (stricmp(sprinfoToken, "ZAXIS")==0) || (stricmp(sprinfoToken, "YAW")==0))
|
|
frameRotAxis = ROTAXIS_Z;
|
|
}
|
|
Z_Free(sprinfoToken);
|
|
|
|
sprinfoToken = M_GetToken(NULL);
|
|
if (sprinfoToken == NULL)
|
|
{
|
|
I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite info or right curly brace should be");
|
|
}
|
|
}
|
|
}
|
|
Z_Free(sprinfoToken);
|
|
}
|
|
|
|
// set fields
|
|
info->pivot[frameFrame].x = frameXPivot;
|
|
info->pivot[frameFrame].y = frameYPivot;
|
|
info->pivot[frameFrame].rotaxis = frameRotAxis;
|
|
}
|
|
|
|
//
|
|
// R_ParseSpriteInfo
|
|
//
|
|
// Parse a SPRTINFO lump.
|
|
//
|
|
static void R_ParseSpriteInfo(boolean spr2)
|
|
{
|
|
spriteinfo_t *info;
|
|
char *sprinfoToken;
|
|
size_t sprinfoTokenLength;
|
|
char newSpriteName[5]; // no longer dynamically allocated
|
|
spritenum_t sprnum = NUMSPRITES;
|
|
//playersprite_t spr2num = NUMPLAYERSPRITES;
|
|
INT32 i;
|
|
INT32 skinnumbers[MAXSKINS]; // >variable 'skinnumbers' set but not used >compile error when commented out
|
|
INT32 foundskins = 0;
|
|
|
|
// Sprite name
|
|
sprinfoToken = M_GetToken(NULL);
|
|
if (sprinfoToken == NULL)
|
|
{
|
|
I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite name should be");
|
|
}
|
|
sprinfoTokenLength = strlen(sprinfoToken);
|
|
if (sprinfoTokenLength != 4)
|
|
{
|
|
I_Error("Error parsing SPRTINFO lump: Sprite name \"%s\" isn't 4 characters long",sprinfoToken);
|
|
}
|
|
else
|
|
{
|
|
memset(&newSpriteName, 0, 5);
|
|
M_Memcpy(newSpriteName, sprinfoToken, sprinfoTokenLength);
|
|
// ^^ we've confirmed that the token is == 4 characters so it will never overflow a 5 byte char buffer
|
|
strupr(newSpriteName); // Just do this now so we don't have to worry about it
|
|
}
|
|
Z_Free(sprinfoToken);
|
|
|
|
if (!spr2)
|
|
{
|
|
for (i = 0; i <= NUMSPRITES; i++)
|
|
{
|
|
if (i == NUMSPRITES)
|
|
I_Error("Error parsing SPRTINFO lump: Unknown sprite name \"%s\"", newSpriteName);
|
|
if (!memcmp(newSpriteName,sprnames[i],4))
|
|
{
|
|
sprnum = static_cast<spritenum_t>(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// allocate a spriteinfo
|
|
info = static_cast<spriteinfo_t*>(Z_Calloc(sizeof(spriteinfo_t), PU_STATIC, NULL));
|
|
info->available = true;
|
|
|
|
// Left Curly Brace
|
|
sprinfoToken = M_GetToken(NULL);
|
|
if (sprinfoToken == NULL)
|
|
{
|
|
I_Error("Error parsing SPRTINFO lump: Unexpected end of file where open curly brace for sprite \"%s\" should be",newSpriteName);
|
|
}
|
|
if (strcmp(sprinfoToken,"{")==0)
|
|
{
|
|
Z_Free(sprinfoToken);
|
|
sprinfoToken = M_GetToken(NULL);
|
|
if (sprinfoToken == NULL)
|
|
{
|
|
I_Error("Error parsing SPRTINFO lump: Unexpected end of file where definition for sprite \"%s\" should be",newSpriteName);
|
|
}
|
|
while (strcmp(sprinfoToken,"}")!=0)
|
|
{
|
|
if (stricmp(sprinfoToken, "SKIN")==0)
|
|
{
|
|
INT32 skinnum;
|
|
char *skinName = NULL;
|
|
if (!spr2)
|
|
I_Error("Error parsing SPRTINFO lump: \"SKIN\" token found outside of a sprite2 definition");
|
|
|
|
Z_Free(sprinfoToken);
|
|
|
|
// Skin name
|
|
sprinfoToken = M_GetToken(NULL);
|
|
if (sprinfoToken == NULL)
|
|
{
|
|
I_Error("Error parsing SPRTINFO lump: Unexpected end of file where skin frame should be");
|
|
}
|
|
|
|
// copy skin name yada yada
|
|
sprinfoTokenLength = strlen(sprinfoToken);
|
|
skinName = (char *)Z_Malloc((sprinfoTokenLength+1)*sizeof(char),PU_STATIC,NULL);
|
|
M_Memcpy(skinName,sprinfoToken,sprinfoTokenLength*sizeof(char));
|
|
skinName[sprinfoTokenLength] = '\0';
|
|
strlwr(skinName);
|
|
Z_Free(sprinfoToken);
|
|
|
|
skinnum = R_AnySkinAvailable(skinName);
|
|
if (skinnum == -1)
|
|
I_Error("Error parsing SPRTINFO lump: Unknown skin \"%s\"", skinName);
|
|
|
|
skinnumbers[foundskins] = skinnum;
|
|
foundskins++;
|
|
}
|
|
else if (stricmp(sprinfoToken, "FRAME")==0)
|
|
{
|
|
R_ParseSpriteInfoFrame(info);
|
|
Z_Free(sprinfoToken);
|
|
if (spr2)
|
|
{
|
|
if (!foundskins)
|
|
I_Error("Error parsing SPRTINFO lump: No skins specified in this sprite2 definition");
|
|
for (i = 0; i < foundskins; i++)
|
|
{
|
|
size_t skinnum = skinnumbers[i];
|
|
skin_t *skin;
|
|
if (allskins[skinnum].localskin)
|
|
skin = &localskins[allskins[skinnum].localnum];
|
|
else
|
|
skin = &skins[allskins[skinnum].localnum];
|
|
M_Memcpy(&skin->sprinfo, info, sizeof(spriteinfo_t));
|
|
}
|
|
}
|
|
else
|
|
M_Memcpy(&spriteinfo[sprnum], info, sizeof(spriteinfo_t));
|
|
}
|
|
else
|
|
{
|
|
I_Error("Error parsing SPRTINFO lump: Unknown keyword \"%s\" in sprite %s",sprinfoToken,newSpriteName);
|
|
}
|
|
|
|
sprinfoToken = M_GetToken(NULL);
|
|
if (sprinfoToken == NULL)
|
|
{
|
|
I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite info or right curly brace for sprite \"%s\" should be",newSpriteName);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
I_Error("Error parsing SPRTINFO lump: Expected \"{\" for sprite \"%s\", got \"%s\"",newSpriteName,sprinfoToken);
|
|
}
|
|
Z_Free(sprinfoToken);
|
|
Z_Free(info);
|
|
}
|
|
|
|
//
|
|
// R_ParseSPRTINFOLump
|
|
//
|
|
// Read a SPRTINFO lump.
|
|
//
|
|
void R_ParseSPRTINFOLump(UINT16 wadNum, UINT16 lumpNum)
|
|
{
|
|
char *sprinfoLump;
|
|
size_t sprinfoLumpLength;
|
|
char *sprinfoText;
|
|
char *sprinfoToken;
|
|
|
|
// Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll
|
|
// need to make a space of memory where I can ensure that it will terminate
|
|
// correctly. Start by loading the relevant data from the WAD.
|
|
sprinfoLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC);
|
|
// If that didn't exist, we have nothing to do here.
|
|
if (sprinfoLump == NULL) return;
|
|
// If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly.
|
|
sprinfoLumpLength = W_LumpLengthPwad(wadNum, lumpNum);
|
|
sprinfoText = (char *)Z_Malloc((sprinfoLumpLength+1)*sizeof(char),PU_STATIC,NULL);
|
|
// Now move the contents of the lump into this new location.
|
|
memmove(sprinfoText,sprinfoLump,sprinfoLumpLength);
|
|
// Make damn well sure the last character in our new memory location is \0.
|
|
sprinfoText[sprinfoLumpLength] = '\0';
|
|
// Finally, free up the memory from the first data load, because we really
|
|
// don't need it.
|
|
Z_Free(sprinfoLump);
|
|
|
|
sprinfoToken = M_GetToken(sprinfoText);
|
|
while (sprinfoToken != NULL)
|
|
{
|
|
CONS_Printf("Attempting to parse le SPRTINFO lump for ya..\n");
|
|
if (!stricmp(sprinfoToken, "SPRITE"))
|
|
R_ParseSpriteInfo(false);
|
|
else if (!stricmp(sprinfoToken, "SPRITE2"))
|
|
R_ParseSpriteInfo(true);
|
|
else
|
|
I_Error("Error parsing SPRTINFO lump: Unknown keyword \"%s\"", sprinfoToken);
|
|
Z_Free(sprinfoToken);
|
|
sprinfoToken = M_GetToken(NULL);
|
|
}
|
|
Z_Free((void *)sprinfoText);
|
|
}
|
|
|
|
//
|
|
// R_LoadSpriteInfoLumps
|
|
//
|
|
// Load and read every SPRTINFO lump from the specified file.
|
|
//
|
|
void R_LoadSpriteInfoLumps(UINT16 wadnum, UINT16 numlumps)
|
|
{
|
|
lumpinfo_t *lumpinfo = wadfiles[wadnum]->lumpinfo;
|
|
UINT16 i;
|
|
char *name;
|
|
|
|
for (i = 0; i < numlumps; i++, lumpinfo++)
|
|
{
|
|
name = lumpinfo->name;
|
|
// Load SPRTINFO and SPR_ lumps as SpriteInfo
|
|
if (!memcmp(name, "SPRTINFO", 8) || !memcmp(name, "SPR_", 4))
|
|
R_ParseSPRTINFOLump(wadnum, i);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Creates a patch.
|
|
// Assumes a PU_PATCH zone memory tag and no user, but can always be set later
|
|
//
|
|
|
|
patch_t *Patch_Create(softwarepatch_t *source, size_t srcsize, void *dest)
|
|
{
|
|
patch_t *patch = static_cast<patch_t*>((dest == NULL) ? Z_Calloc(sizeof(patch_t), PU_PATCH, NULL) : dest);
|
|
|
|
if (source)
|
|
{
|
|
INT32 col, colsize;
|
|
size_t size = sizeof(INT32) * SHORT(source->width);
|
|
size_t offs = (sizeof(INT16) * 4) + size;
|
|
|
|
patch->width = SHORT(source->width);
|
|
patch->height = SHORT(source->height);
|
|
patch->leftoffset = SHORT(source->leftoffset);
|
|
patch->topoffset = SHORT(source->topoffset);
|
|
patch->columnofs = static_cast<INT32*>(Z_Calloc(size, PU_PATCH_DATA, NULL));
|
|
|
|
for (col = 0; col < source->width; col++)
|
|
{
|
|
// This makes the column offsets relative to the column data itself,
|
|
// instead of the entire patch data
|
|
patch->columnofs[col] = LONG(source->columnofs[col]) - offs;
|
|
}
|
|
|
|
if (!srcsize)
|
|
I_Error("Patch_Create: no source size!");
|
|
|
|
colsize = (INT32)(srcsize) - (INT32)offs;
|
|
if (colsize <= 0)
|
|
I_Error("Patch_Create: no column data!");
|
|
|
|
patch->columns = static_cast<UINT8*>(Z_Calloc(colsize, PU_PATCH_DATA, NULL));
|
|
M_Memcpy(patch->columns, ((UINT8 *)source + LONG(source->columnofs[0])), colsize);
|
|
}
|
|
|
|
return patch;
|
|
}
|
|
|
|
//
|
|
// Frees a patch from memory.
|
|
//
|
|
|
|
static void Patch_FreeData(patch_t *patch)
|
|
{
|
|
#ifdef HWRENDER
|
|
if (patch->hardware)
|
|
HWR_FreeTexture(patch);
|
|
#endif
|
|
|
|
#ifdef ROTSPRITE
|
|
/*if (patch->rotated)
|
|
{
|
|
rotsprite_t *rotsprite = patch->rotated;
|
|
INT32 i = 0;
|
|
|
|
for (; i < rotsprite->angles; i++)
|
|
{
|
|
if (rotsprite->patches[i])
|
|
Patch_Free(rotsprite->patches[i]);
|
|
}
|
|
|
|
Z_Free(rotsprite->patches);
|
|
Z_Free(rotsprite);
|
|
}*/
|
|
#endif
|
|
|
|
if (patch->columnofs)
|
|
Z_Free(patch->columnofs);
|
|
if (patch->columns)
|
|
Z_Free(patch->columns);
|
|
}
|
|
|
|
void Patch_Free(patch_t *patch)
|
|
{
|
|
Patch_FreeData(patch);
|
|
Z_Free(patch);
|
|
}
|
|
|
|
//
|
|
// Frees patches with a tag range.
|
|
//
|
|
|
|
static boolean Patch_FreeTagsCallback(void *mem)
|
|
{
|
|
patch_t *patch = (patch_t *)mem;
|
|
Patch_FreeData(patch);
|
|
return true;
|
|
}
|
|
|
|
void Patch_FreeTags(INT32 lowtag, INT32 hightag)
|
|
{
|
|
Z_IterateTags(lowtag, hightag, Patch_FreeTagsCallback);
|
|
}
|
|
|
|
#ifdef HWRENDER
|
|
//
|
|
// Allocates a hardware patch.
|
|
//
|
|
|
|
void *Patch_AllocateHardwarePatch(patch_t *patch)
|
|
{
|
|
if (!patch->hardware)
|
|
{
|
|
GLPatch_t *glPatch = static_cast<GLPatch_t*>(Z_Calloc(sizeof(GLPatch_t), PU_HWRPATCHINFO, &patch->hardware));
|
|
glPatch->mipmap = static_cast<GLMipmap_t*>(Z_Calloc(sizeof(GLMipmap_t), PU_HWRPATCHINFO, &glPatch->mipmap));
|
|
}
|
|
return (void *)(patch->hardware);
|
|
}
|
|
|
|
//
|
|
// Creates a hardware patch.
|
|
//
|
|
|
|
void *Patch_CreateGL(patch_t *patch)
|
|
{
|
|
GLPatch_t *glPatch = (GLPatch_t *)Patch_AllocateHardwarePatch(patch);
|
|
if (!glPatch->mipmap->data) // Run HWR_MakePatch in all cases, to recalculate some things
|
|
HWR_MakePatch(patch, glPatch, glPatch->mipmap, false);
|
|
return glPatch;
|
|
}
|
|
#endif // HWRENDER
|
|
|
|
// 1 MB should be enough for most cases
|
|
static std::vector<UINT8> imgbuf(1024 * 1024);
|
|
|
|
//
|
|
// R_MaskedFlatToPatch
|
|
//
|
|
// Convert raw pixels to a patch.
|
|
//
|
|
void *R_PixelsToPatch(UINT8 *raw, INT16 width, INT16 height, INT16 leftoffset, INT16 topoffset, size_t *destsize)
|
|
{
|
|
INT16 x, y;
|
|
UINT8 *img, *imgptr;
|
|
UINT8 *colpointers, *startofspan;
|
|
size_t size = 0;
|
|
|
|
if (!raw)
|
|
return NULL;
|
|
|
|
// Allocate a staging buffer with the maximum size needed for a patch of the same size as the input.
|
|
|
|
// round up to nearest multiple of 254-pixel posts, plus 1 more 254-pixel post for paranoia reasons
|
|
size_t maxcolumnsize = (2 + (height - 1) / 256) * 256;
|
|
// the patch header, and width columns of the max column size
|
|
size_t maxoutsize = maxcolumnsize * width + (8 + 4 * width);
|
|
// so, a 512x512 flat should maximally need 393,760 (384.53 KiB) bytes.
|
|
// quite a bit smaller than 64 megabytes, and much less annoying to the windows debug allocator!
|
|
while (imgbuf.size() < maxoutsize)
|
|
{
|
|
imgbuf.resize(imgbuf.size() * 2);
|
|
}
|
|
|
|
imgptr = imgbuf.data();
|
|
|
|
// Write image size and offset
|
|
WRITEINT16(imgptr, width);
|
|
WRITEINT16(imgptr, height);
|
|
WRITEINT16(imgptr, leftoffset);
|
|
WRITEINT16(imgptr, topoffset);
|
|
|
|
// Leave placeholder to column pointers
|
|
colpointers = imgptr;
|
|
imgptr += width*4;
|
|
|
|
// Write columns
|
|
for (x = 0; x < width; x++)
|
|
{
|
|
int lastStartY = 0;
|
|
int spanSize = 0;
|
|
startofspan = NULL;
|
|
|
|
// Write column pointer
|
|
WRITEINT32(colpointers, imgptr - imgbuf.data());
|
|
|
|
// Write pixels
|
|
for (y = 0; y < height; y++)
|
|
{
|
|
UINT8 pixel = raw[((y * width) + x)];
|
|
UINT8 opaque = (pixel != 0xf7); // If not 247 (0xf7), we have a pixel
|
|
|
|
// End span if we have a transparent pixel
|
|
if (!opaque)
|
|
{
|
|
if (startofspan)
|
|
WRITEUINT8(imgptr, 0);
|
|
startofspan = NULL;
|
|
continue;
|
|
}
|
|
|
|
// Start new column if we need to
|
|
if (!startofspan || spanSize == 255)
|
|
{
|
|
int writeY = y;
|
|
|
|
// If we reached the span size limit, finish the previous span
|
|
if (startofspan)
|
|
WRITEUINT8(imgptr, 0);
|
|
|
|
if (y > 254)
|
|
{
|
|
// Make sure we're aligned to 254
|
|
if (lastStartY < 254)
|
|
{
|
|
WRITEUINT8(imgptr, 254);
|
|
WRITEUINT8(imgptr, 0);
|
|
imgptr += 2;
|
|
lastStartY = 254;
|
|
}
|
|
|
|
// Write stopgap empty spans if needed
|
|
writeY = y - lastStartY;
|
|
|
|
while (writeY > 254)
|
|
{
|
|
WRITEUINT8(imgptr, 254);
|
|
WRITEUINT8(imgptr, 0);
|
|
imgptr += 2;
|
|
writeY -= 254;
|
|
}
|
|
}
|
|
|
|
startofspan = imgptr;
|
|
WRITEUINT8(imgptr, writeY);
|
|
imgptr += 2;
|
|
spanSize = 0;
|
|
|
|
lastStartY = y;
|
|
}
|
|
|
|
// Write the pixel
|
|
WRITEUINT8(imgptr, pixel);
|
|
spanSize++;
|
|
startofspan[1] = spanSize;
|
|
}
|
|
|
|
if (startofspan)
|
|
WRITEUINT8(imgptr, 0);
|
|
|
|
WRITEUINT8(imgptr, 0xFF);
|
|
}
|
|
|
|
size = imgptr-imgbuf.data();
|
|
img = static_cast<UINT8*>(Z_Malloc(size, PU_STATIC, NULL));
|
|
memcpy(img, imgbuf.data(), size);
|
|
|
|
if (destsize != NULL)
|
|
*destsize = size;
|
|
|
|
patch_t *converted = Patch_Create((softwarepatch_t *)img, size, NULL);
|
|
#ifdef HWRENDER
|
|
Patch_CreateGL(converted);
|
|
#endif
|
|
Z_Free(img);
|
|
|
|
return converted;
|
|
}
|
|
|
|
//
|
|
// R_MaskedFlatToPatch
|
|
//
|
|
// Convert a masked flat to a patch.
|
|
// Explanation of "masked" flats in R_PatchToMaskedFlat.
|
|
//
|
|
void *R_MaskedFlatToPatch(UINT16 *raw, INT16 width, INT16 height, INT16 leftoffset, INT16 topoffset, size_t *destsize)
|
|
{
|
|
INT16 x, y;
|
|
UINT8 *img, *imgptr;
|
|
UINT8 *colpointers, *startofspan;
|
|
size_t size = 0;
|
|
|
|
if (!raw)
|
|
return NULL;
|
|
|
|
// Allocate a staging buffer with the maximum size needed for a patch of the same size as the input.
|
|
|
|
// round up to nearest multiple of 254-pixel posts, plus 1 more 254-pixel post for paranoia reasons
|
|
size_t maxcolumnsize = (2 + (height - 1) / 256) * 256;
|
|
// the patch header, and width columns of the max column size
|
|
size_t maxoutsize = maxcolumnsize * width + (8 + 4 * width);
|
|
// so, a 512x512 flat should maximally need 393,760 (384.53 KiB) bytes.
|
|
// quite a bit smaller than 64 megabytes, and much less annoying to the windows debug allocator!
|
|
while (imgbuf.size() < maxoutsize)
|
|
{
|
|
imgbuf.resize(imgbuf.size() * 2);
|
|
}
|
|
|
|
imgptr = imgbuf.data();
|
|
|
|
// Write image size and offset
|
|
WRITEINT16(imgptr, width);
|
|
WRITEINT16(imgptr, height);
|
|
WRITEINT16(imgptr, leftoffset);
|
|
WRITEINT16(imgptr, topoffset);
|
|
|
|
// Leave placeholder to column pointers
|
|
colpointers = imgptr;
|
|
imgptr += width*4;
|
|
|
|
// Write columns
|
|
for (x = 0; x < width; x++)
|
|
{
|
|
int lastStartY = 0;
|
|
int spanSize = 0;
|
|
startofspan = NULL;
|
|
|
|
// Write column pointer
|
|
WRITEINT32(colpointers, imgptr - imgbuf.data());
|
|
|
|
// Write pixels
|
|
for (y = 0; y < height; y++)
|
|
{
|
|
UINT16 pixel = raw[((y * width) + x)];
|
|
UINT8 paletteIndex = (pixel & 0xFF);
|
|
UINT8 opaque = (pixel != 0xFF00); // If 1, we have a pixel
|
|
|
|
// End span if we have a transparent pixel
|
|
if (!opaque)
|
|
{
|
|
if (startofspan)
|
|
WRITEUINT8(imgptr, 0);
|
|
startofspan = NULL;
|
|
continue;
|
|
}
|
|
|
|
// Start new column if we need to
|
|
if (!startofspan || spanSize == 255)
|
|
{
|
|
int writeY = y;
|
|
|
|
// If we reached the span size limit, finish the previous span
|
|
if (startofspan)
|
|
WRITEUINT8(imgptr, 0);
|
|
|
|
if (y > 254)
|
|
{
|
|
// Make sure we're aligned to 254
|
|
if (lastStartY < 254)
|
|
{
|
|
WRITEUINT8(imgptr, 254);
|
|
WRITEUINT8(imgptr, 0);
|
|
imgptr += 2;
|
|
lastStartY = 254;
|
|
}
|
|
|
|
// Write stopgap empty spans if needed
|
|
writeY = y - lastStartY;
|
|
|
|
while (writeY > 254)
|
|
{
|
|
WRITEUINT8(imgptr, 254);
|
|
WRITEUINT8(imgptr, 0);
|
|
imgptr += 2;
|
|
writeY -= 254;
|
|
}
|
|
}
|
|
|
|
startofspan = imgptr;
|
|
WRITEUINT8(imgptr, writeY);
|
|
imgptr += 2;
|
|
spanSize = 0;
|
|
|
|
lastStartY = y;
|
|
}
|
|
|
|
// Write the pixel
|
|
WRITEUINT8(imgptr, paletteIndex);
|
|
spanSize++;
|
|
startofspan[1] = spanSize;
|
|
}
|
|
|
|
if (startofspan)
|
|
WRITEUINT8(imgptr, 0);
|
|
|
|
WRITEUINT8(imgptr, 0xFF);
|
|
}
|
|
|
|
size = imgptr-imgbuf.data();
|
|
img = static_cast<UINT8*>(Z_Malloc(size, PU_STATIC, NULL));
|
|
memcpy(img, imgbuf.data(), size);
|
|
|
|
if (destsize != NULL)
|
|
*destsize = size;
|
|
|
|
patch_t *converted = Patch_Create((softwarepatch_t *)img, size, NULL);
|
|
#ifdef HWRENDER
|
|
Patch_CreateGL(converted);
|
|
#endif
|
|
Z_Free(img);
|
|
|
|
return converted;
|
|
}
|
|
|
|
UINT16 R_GetPatchPixel(patch_t *patch, INT32 x, INT32 y, boolean flip)
|
|
{
|
|
fixed_t ofs;
|
|
column_t *column;
|
|
UINT8 *source = NULL;
|
|
INT16 width;
|
|
|
|
if (patch == NULL)
|
|
I_Error("GetPatchPixel: patch == NULL");
|
|
|
|
width = patch->width;
|
|
|
|
if (x >= 0 && x < width)
|
|
{
|
|
INT32 colx = flip ? (width-1)-x : x;
|
|
INT32 topdelta, prevdelta = -1;
|
|
|
|
column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[colx]));
|
|
|
|
while (column->topdelta != 0xff)
|
|
{
|
|
topdelta = column->topdelta;
|
|
|
|
if (topdelta <= prevdelta)
|
|
topdelta += prevdelta;
|
|
|
|
prevdelta = topdelta;
|
|
|
|
ofs = (y - topdelta);
|
|
|
|
if (y >= topdelta && ofs < column->length)
|
|
{
|
|
source = (UINT8 *)(column) + 3;
|
|
return source[ofs];
|
|
}
|
|
|
|
column = (column_t *)((UINT8 *)column + column->length + 4);
|
|
}
|
|
}
|
|
|
|
return 0xFF00;
|
|
}
|
|
|
|
void R_PatchToPixels(patch_t *patch, UINT8 *dst)
|
|
{
|
|
const INT16 width = patch->width;
|
|
|
|
// init out destination to cyan so we can easily check for it when we convert it back to a patch
|
|
memset(dst, 0xf7, (width * patch->height)); // should this be here or should this be taken care of before passing it?
|
|
|
|
for (INT32 x = 0; x < width; ++x)
|
|
{
|
|
INT32 y = 0;
|
|
INT32 prevdelta = -1;
|
|
|
|
column_t *column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[x]));
|
|
|
|
while (column->topdelta != 0xff)
|
|
{
|
|
INT32 topdelta = column->topdelta;
|
|
|
|
if (topdelta <= prevdelta)
|
|
topdelta += prevdelta;
|
|
|
|
prevdelta = topdelta;
|
|
|
|
for (; y-topdelta < column->length; ++y)
|
|
{
|
|
UINT8 pixel = 0;
|
|
|
|
if (y >= topdelta)
|
|
{
|
|
UINT8 *source = (UINT8*)(column) + 3;
|
|
pixel = source[y-topdelta];
|
|
}
|
|
|
|
dst[(y*width) + x] = pixel;
|
|
}
|
|
|
|
column = (column_t *)((UINT8 *)column + column->length + 4);
|
|
}
|
|
}
|
|
}
|