From 2536eca01d22c4a7b0938e9af27e96e059a30dd9 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers <coelckers@zdoom.fake> Date: Sun, 20 Aug 2006 12:55:46 +0000 Subject: [PATCH] - Changed: Patch and IMGZ textures now initialize their dimensions upon creation. The lump is open anyway at that time so deferring this action until the information is needed doesn't give any speed improvements. Now GetDimensions and all its associated overhead is gone. - Added support for TGA textures. It can handle all of the common variations of this format. - Changed: GI_PAGESARERAW is no longer checked. It wasn't really necessary before because the chance of texture misidentification is absolutely minimal. But raw pages are now restricted to textures of type TEX_MiscPatch only. - Changed the automap parchment to use a regular texture. The previous FAutomapTexture is only used as a last resort fallback now. If the code finds a recognizable graphic it will create a proper texture for it now. - Fixed: Flats were only auto-scaled when in Doom flat format. - Fixed: FMultiPatchTexture::CheckForHacks blindly assumed that all patches were FPstchTextures. Since the texture code does not have any type information I added a new flag bIsPatch for this purpose. - Moved all texture classes into their own source files and created a new subdirectory 'textures' for that. - Cleaned up the texture management code and added some stricter checks for the validity of Doom patches. The old code liked to crash when being passed some non-graphic data. SVN r300 (trunk) --- Makefile.linux | 2 +- Makefile.mingw | 2 +- docs/rh-log.txt | 24 + src/am_map.cpp | 91 +- src/g_heretic/a_hereticplayer.cpp | 2 +- src/r_data.cpp | 2892 +--------------------------- src/r_data.h | 101 +- src/r_defs.h | 22 +- src/textures/automaptexture.cpp | 122 ++ src/textures/buildtexture.cpp | 288 +++ src/textures/canvastexture.cpp | 167 ++ src/textures/flattexture.cpp | 152 ++ src/textures/imgztexture.cpp | 224 +++ src/textures/jpegtexture.cpp | 316 +++ src/textures/multipatchtexture.cpp | 534 +++++ src/textures/patchtexture.cpp | 400 ++++ src/textures/pngtexture.cpp | 419 ++++ src/textures/rawpagetexture.cpp | 201 ++ src/textures/texture.cpp | 423 ++++ src/textures/tgatexture.cpp | 398 ++++ src/textures/warptexture.cpp | 233 +++ thingdef_specials.gperf | 13 +- zdoom.vcproj | 56 + 23 files changed, 4099 insertions(+), 2983 deletions(-) create mode 100644 src/textures/automaptexture.cpp create mode 100644 src/textures/buildtexture.cpp create mode 100644 src/textures/canvastexture.cpp create mode 100644 src/textures/flattexture.cpp create mode 100644 src/textures/imgztexture.cpp create mode 100644 src/textures/jpegtexture.cpp create mode 100644 src/textures/multipatchtexture.cpp create mode 100644 src/textures/patchtexture.cpp create mode 100644 src/textures/pngtexture.cpp create mode 100644 src/textures/rawpagetexture.cpp create mode 100644 src/textures/texture.cpp create mode 100644 src/textures/tgatexture.cpp create mode 100644 src/textures/warptexture.cpp diff --git a/Makefile.linux b/Makefile.linux index bb89f89e0..1aaaf17d9 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -19,7 +19,7 @@ CFLAGS += -Dstricmp=strcasecmp -Dstrnicmp=strncasecmp -DNEED_STRUPR LDFLAGS += -lFLAC++ -lFLAC -lz -ljpeg -lfmod `sdl-config --libs` NASMFLAGS += -f elf -DM_TARGET_LINUX -SRCDIRS = src/ $(addprefix src/,g_doom/ g_heretic/ g_hexen/ g_raven/ g_shared/ g_strife/ oplsynth/ sound/ sdl/) +SRCDIRS = src/ $(addprefix src/,g_doom/ g_heretic/ g_hexen/ g_raven/ g_shared/ g_strife/ oplsynth/ sound/ sdl/ textures/ ) VPATH = $(SRCDIRS) INCLUDES = $(addprefix -I,$(SRCDIRS)) CFLAGS += $(INCLUDES) diff --git a/Makefile.mingw b/Makefile.mingw index 55e634c15..79584f381 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -52,7 +52,7 @@ ifeq ($(CONFIG),Release) TARGET = $(RELEASETARGET) endif -SRCDIRS = src/ $(addprefix src/,g_doom/ g_heretic/ g_hexen/ g_raven/ g_shared/ g_strife/ oplsynth/ sound/ win32/) +SRCDIRS = src/ $(addprefix src/,g_doom/ g_heretic/ g_hexen/ g_raven/ g_shared/ g_strife/ oplsynth/ sound/ win32/ textures/) VPATH = $(SRCDIRS) CPPSRCS = $(wildcard $(addsuffix *.cpp,$(SRCDIRS))) diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 64b8fcdd9..b00085513 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,3 +1,27 @@ + +August 20, 2006 (Changes by Graf Zahl) +- Changed: Patch and IMGZ textures now initialize their dimensions upon creation. + The lump is open anyway at that time so deferring this action until the information + is needed doesn't give any speed improvements. Now GetDimensions and all its + associated overhead is gone. +- Added support for TGA textures. It can handle all of the common variations + of this format. +- Changed: GI_PAGESARERAW is no longer checked. It wasn't really necessary before + because the chance of texture misidentification is absolutely minimal. + But raw pages are now restricted to textures of type TEX_MiscPatch only. +- Changed the automap parchment to use a regular texture. The previous + FAutomapTexture is only used as a last resort fallback now. If the code + finds a recognizable graphic it will create a proper texture for it now. +- Fixed: Flats were only auto-scaled when in Doom flat format. +- Fixed: FMultiPatchTexture::CheckForHacks blindly assumed that all patches + were FPstchTextures. Since the texture code does not have any type information + I added a new flag bIsPatch for this purpose. +- Moved all texture classes into their own source files and created a new + subdirectory 'textures' for that. +- Cleaned up the texture management code and added some stricter checks for + the validity of Doom patches. The old code liked to crash when being passed + some non-graphic data. + August 19, 2006 (Changes by Graf Zahl) - Added custom fail messages to the puzzle items. diff --git a/src/am_map.cpp b/src/am_map.cpp index cc35555f4..48911879e 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -320,24 +320,7 @@ static int markpointnum = 0; // next point to be assigned static int followplayer = 1; // specifies whether to follow the player around -class FAutomapTexture : public FTexture -{ -public: - FAutomapTexture (int lumpnum); - ~FAutomapTexture (); - - const BYTE *GetColumn (unsigned int column, const Span **spans_out); - const BYTE *GetPixels (); - void Unload (); - void MakeTexture (); - -private: - BYTE *Pixels; - Span DummySpan[2]; - int LumpNum; -}; - -static FAutomapTexture *mapback; // the automap background +static FTexture *mapback; // the automap background static fixed_t mapystart=0; // y-value for the start of the map bitmap...used in the parallax stuff. static fixed_t mapxstart=0; //x-value for the bitmap. @@ -784,7 +767,7 @@ void AM_loadPics () i = Wads.CheckNumForName ("AUTOPAGE"); if (i >= 0) { - mapback = new FAutomapTexture (i); + mapback = FTexture::CreateTexture(i, FTexture::TEX_Autopage); } } } @@ -2201,73 +2184,3 @@ void AM_Drawer () AM_drawMarks(); } -FAutomapTexture::FAutomapTexture (int lumpnum) -: Pixels(NULL), LumpNum(lumpnum) -{ - UseType = TEX_MiscPatch; - Width = 320; - Height = Wads.LumpLength(lumpnum) / 320; - CalcBitSize (); - - DummySpan[0].TopOffset = 0; - DummySpan[0].Length = Height; - DummySpan[1].TopOffset = 0; - DummySpan[1].Length = 0; -} - -FAutomapTexture::~FAutomapTexture () -{ - Unload (); -} - -void FAutomapTexture::Unload () -{ - if (Pixels != NULL) - { - delete[] Pixels; - Pixels = NULL; - } -} - -void FAutomapTexture::MakeTexture () -{ - int x, y; - FMemLump data = Wads.ReadLump (LumpNum); - const BYTE *indata = (const BYTE *)data.GetMem(); - - Pixels = new BYTE[Width * Height]; - - for (x = 0; x < Width; ++x) - { - for (y = 0; y < Height; ++y) - { - Pixels[x*Height+y] = indata[x+320*y]; - } - } -} - -const BYTE *FAutomapTexture::GetPixels () -{ - if (Pixels == NULL) - { - MakeTexture (); - } - return Pixels; -} - -const BYTE *FAutomapTexture::GetColumn (unsigned int column, const Span **spans_out) -{ - if (Pixels == NULL) - { - MakeTexture (); - } - if ((unsigned)column >= (unsigned)Width) - { - column %= Width; - } - if (spans_out != NULL) - { - *spans_out = DummySpan; - } - return Pixels + column*Height; -} diff --git a/src/g_heretic/a_hereticplayer.cpp b/src/g_heretic/a_hereticplayer.cpp index fe1f223bc..e3c4b2620 100644 --- a/src/g_heretic/a_hereticplayer.cpp +++ b/src/g_heretic/a_hereticplayer.cpp @@ -148,7 +148,7 @@ void AHereticPlayer::GiveDefaultInventory () wand = player->mo->GiveInventoryType (PClass::FindClass ("GoldWand")); // Adding the gold wand automatically adds its ammo ammo = player->mo->FindInventory (PClass::FindClass ("GoldWandAmmo")); - ammo->Amount = 50; + if (ammo != NULL) ammo->Amount = 50; player->ReadyWeapon = player->PendingWeapon = static_cast<AWeapon *> (wand); } } diff --git a/src/r_data.cpp b/src/r_data.cpp index 8614d106e..98eedff4e 100644 --- a/src/r_data.cpp +++ b/src/r_data.cpp @@ -24,22 +24,10 @@ // //----------------------------------------------------------------------------- -// On the Alpha, accessing the shorts directly if they aren't aligned on a -// 4-byte boundary causes unaligned access warnings. Why it does this at -// all and only while initing the textures is beyond me. - -#ifdef ALPHA -#define SAFESHORT(s) ((short)(((byte *)&(s))[0] + ((byte *)&(s))[1] * 256)) -#else -#define SAFESHORT(s) LittleShort(s) -#endif - #include <stddef.h> #include <malloc.h> #include <stdio.h> -#include "r_jpeg.h" - #include "i_system.h" #include "m_alloc.h" @@ -66,23 +54,9 @@ #include "cmdlib.h" #include "templates.h" -extern float LastFOV; - static void R_InitPatches (); - -// [RH] Just a format I invented to avoid WinTex's palette remapping -// when I wanted to insert some alpha maps. - -struct FIMGZTexture::ImageHeader -{ - BYTE Magic[4]; - WORD Width; - WORD Height; - SWORD LeftOffset; - SWORD TopOffset; - BYTE Compression; - BYTE Reserved[11]; -}; +void R_InitBuildTiles(); +void R_DeinitBuildTiles(); // // Graphics. @@ -98,7 +72,6 @@ byte** warpedflats; int* flatwarpedwhen; -static TArray<BYTE *> BuildTileFiles; FTextureManager TexMan; FTextureManager::FTextureManager () @@ -106,6 +79,7 @@ FTextureManager::FTextureManager () memset (HashFirst, -1, sizeof(HashFirst)); // Texture 0 is a dummy texture used to indicate "no texture" AddTexture (new FDummyTexture); + } FTextureManager::~FTextureManager () @@ -233,221 +207,17 @@ int FTextureManager::AddTexture (FTexture *texture) return trans; } - -// Examines the lump contents to decide what type of texture to create, -// and creates the texture. -FTexture * FTextureManager::DoCreateTexture (int lumpnum, int usetype) -{ - FTexture *out = NULL; - enum { t_patch, t_raw, t_imgz, t_png } type = t_patch; - union - { - DWORD dw; - WORD w[2]; - BYTE b[4]; - } first4bytes; - - // Must check the length of the lump. Zero length flat markers (F1_START etc.) will come through here. - // 13 is the minimum length of anything valid (i.e a 1x1 pixel Doom patch.) - if (lumpnum < 0 || Wads.LumpLength(lumpnum)<13) - { - return NULL; - } - else - { - FWadLump data = Wads.OpenLumpNum (lumpnum); - - data.Read (first4bytes.b, 4); - if (first4bytes.dw == MAKE_ID('I','M','G','Z')) - { - type = t_imgz; - } - else if (first4bytes.dw == MAKE_ID(137,'P','N','G')) - { - DWORD width, height; - BYTE bitdepth, colortype, compression, filter, interlace; - - // This is most likely a PNG, but make sure. (Note that if the - // first 4 bytes match, but later bytes don't, we assume it's - // a corrupt PNG.) - data.Read (first4bytes.b, 4); - if (first4bytes.dw != MAKE_ID(13,10,26,10)) return NULL; - data.Read (first4bytes.b, 4); - if (first4bytes.dw != MAKE_ID(0,0,0,13)) return NULL; - data.Read (first4bytes.b, 4); - if (first4bytes.dw != MAKE_ID('I','H','D','R')) return NULL; - - // The PNG looks valid so far. Check the IHDR to make sure it's a - // type of PNG we support. - data >> width >> height - >> bitdepth >> colortype >> compression >> filter >> interlace; - - if (compression != 0 || filter != 0 || interlace > 1) - { - return NULL; - } - if (!((1 << colortype) & 0x5D)) - { - return NULL; - } - if (!((1 << bitdepth) & 0x116)) - { - return NULL; - } - - // Just for completeness, make sure the PNG has something more than an - // IHDR. - data.Seek (4, SEEK_CUR); - data.Read (first4bytes.b, 4); - if (first4bytes.dw == 0) - { - data.Read (first4bytes.b, 4); - if (first4bytes.dw == MAKE_ID('I','E','N','D')) - { - return NULL; - } - } - - type = t_png; - out = new FPNGTexture (lumpnum, BigLong((int)width), BigLong((int)height), - bitdepth, colortype, interlace); - } - else if (first4bytes.b[0] == 0xFF && first4bytes.b[1] == 0xD8 && first4bytes.b[2] == 0xFF) - { - // JPEG, I presume - - // Find the SOFn marker to extract the image dimensions, - // where n is 0, 1, or 2 (other types are unsupported). - while ((unsigned)first4bytes.b[3] - 0xC0 >= 3) - { - if (data.Read (first4bytes.w, 2) != 2) - { - return NULL; - } - data.Seek (BigShort(first4bytes.w[0]) - 2, SEEK_CUR); - if (data.Read (first4bytes.b + 2, 2) != 2 || first4bytes.b[2] != 0xFF) - { - return NULL; - } - } - if (data.Read (first4bytes.b, 3) != 3) - { - return NULL; - } - if (BigShort (first4bytes.w[0]) <5) - { - return NULL; - } - if (data.Read (first4bytes.b, 4) != 4) - { - return NULL; - } - type = t_png; - out = new FJPEGTexture (lumpnum, BigShort(first4bytes.w[1]), BigShort(first4bytes.w[0])); - } - else if (usetype == FTexture::TEX_Flat) - { - // allow PNGs as flats but not Doom patches. - return NULL; - } - else if ((gameinfo.flags & GI_PAGESARERAW) && data.GetLength() == 64000) - { - // This is probably a raw page graphic, but do some checking to be sure - patch_t *foo; - int height; - int width; - - foo = (patch_t *)M_Malloc (data.GetLength()); - data.Seek (-4, SEEK_CUR); - data.Read (foo, data.GetLength()); - - height = LittleShort(foo->height); - width = LittleShort(foo->width); - - if (height > 0 && height < 510 && width > 0 && width < 15997) - { - // 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) - { - DWORD ofs = LittleLong(foo->columnofs[x]); - if (ofs == (DWORD)width * 4 + 8) - { - gapAtStart = false; - } - else if (ofs >= 64000-1) // Need one byte for an empty column - { - break; - } - else - { - // Ensure this column does not extend beyond the end of the patch - const BYTE *foo2 = (const BYTE *)foo; - while (ofs < 64000) - { - if (foo2[ofs] == 255) - { - break; - } - ofs += foo2[ofs+1] + 4; - } - if (ofs >= 64000) - { - break; - } - } - } - if (gapAtStart || (x != width)) - { - type = t_raw; - } - } - else - { - type = t_raw; - } - free (foo); - } - } - switch (type) - { - default: - { // Check patch sizes for sanity - WORD width = LittleShort(first4bytes.w[0]); - WORD height = LittleShort(first4bytes.w[1]); - - if (width <= 2048 && height <= 2048) - { - out = new FPatchTexture (lumpnum, FTexture::TEX_MiscPatch); - } - } - break; - case t_raw: out = new FRawPageTexture (lumpnum); break; - case t_imgz: out = new FIMGZTexture (lumpnum); break; - case t_png: break; - } - if (out != NULL) - { - if (usetype != FTexture::TEX_Any) - { - out->UseType = usetype; - } - } - return out; -} - // Calls DoCreateTexture and adds the texture to the manager. int FTextureManager::CreateTexture (int lumpnum, int usetype) { - FTexture *out = DoCreateTexture(lumpnum, usetype); + FTexture *out = FTexture::CreateTexture(lumpnum, usetype); if (out != NULL) return AddTexture (out); - return -1; + else + { + Printf (TEXTCOLOR_ORANGE "Invalid data encountered for texture %s\n", Wads.GetLumpFullName(lumpnum)); + return -1; + } } void FTextureManager::ReplaceTexture (int picnum, FTexture *newtexture, bool free) @@ -485,136 +255,13 @@ int FTextureManager::AddPatch (const char *patchname, int namespc) return -1; } - return CreateTexture (lumpnum); + return CreateTexture (lumpnum, FTexture::TEX_MiscPatch); } -void FTextureManager::AddFlats () +void FTextureManager::AddGroup(const char * startlump, const char * endlump, int ns, int usetype) { - int firstflat = Wads.GetNumForName ("F_START") + 1; - int lastflat = Wads.GetNumForName ("F_END") - 1; - int i; - - for (i = firstflat; i <= lastflat; ++i) - { - // Support PNGs as flats! - if (CreateTexture (i, FTexture::TEX_Flat) == -1) - { - AddTexture (new FFlatTexture (i)); - } - } -} - -void FTextureManager::AddSprites () -{ - int firstsprite = Wads.GetNumForName ("S_START") + 1; - int lastsprite = Wads.GetNumForName ("S_END") - 1; - int i; - - for (i = firstsprite; i <= lastsprite; ++i) - { - CreateTexture (i, FTexture::TEX_Sprite); - } -} - -void FTextureManager::AddTiles (void *tiles) -{ -// int numtiles = LittleLong(((DWORD *)tiles)[1]); // This value is not reliable - int tilestart = LittleLong(((DWORD *)tiles)[2]); - int tileend = LittleLong(((DWORD *)tiles)[3]); - const WORD *tilesizx = &((const WORD *)tiles)[8]; - const WORD *tilesizy = &tilesizx[tileend - tilestart + 1]; - const DWORD *picanm = (const DWORD *)&tilesizy[tileend - tilestart + 1]; - BYTE *tiledata = (BYTE *)&picanm[tileend - tilestart + 1]; - - for (int i = tilestart; i <= tileend; ++i) - { - int pic = i - tilestart; - int width = LittleShort(tilesizx[pic]); - int height = LittleShort(tilesizy[pic]); - DWORD anm = LittleLong(picanm[pic]); - int xoffs = (SBYTE)((anm >> 8) & 255) + width/2; - int yoffs = (SBYTE)((anm >> 16) & 255) + height/2; - int size = width*height; - int texnum; - FTexture *tex; - - if (width <= 0 || height <= 0) continue; - - tex = new FBuildTexture (i, tiledata, width, height, xoffs, yoffs); - texnum = AddTexture (tex); - while (size > 0) - { - *tiledata = 255 - *tiledata; - tiledata++; - size--; - } - - if ((picanm[pic] & 63) && (picanm[pic] & 192)) - { - int type, speed; - - switch (picanm[pic] & 192) - { - case 64: type = 2; break; - case 128: type = 0; break; - case 192: type = 1; break; - default: type = 0; break; // Won't happen, but GCC bugs me if I don't put this here. - } - - speed = (anm >> 24) & 15; - speed = MAX (1, (1 << speed) * 1000 / 120); // Convert from 120 Hz to 1000 Hz. - - R_AddSimpleAnim (texnum, picanm[pic] & 63, type, speed); - } - - // Blood's rotation types: - // 0 - Single - // 1 - 5 Full - // 2 - 8 Full - // 3 - Bounce (looks no different from Single; seems to signal bouncy sprites) - // 4 - 5 Half (not used in game) - // 5 - 3 Flat (not used in game) - // 6 - Voxel - // 7 - Spin Voxel - - int rotType = (anm >> 28) & 7; - if (rotType == 1) - { - spriteframe_t rot; - rot.Texture[0] = texnum; - rot.Texture[1] = texnum; - for (int j = 1; j < 4; ++j) - { - rot.Texture[j*2] = texnum + j; - rot.Texture[j*2+1] = texnum + j; - rot.Texture[16-j*2] = texnum + j; - rot.Texture[17-j*2] = texnum + j; - } - rot.Texture[8] = texnum + 4; - rot.Texture[9] = texnum + 4; - rot.Flip = 0x00FC; - tex->Rotations = SpriteFrames.Push (rot); - } - else if (rotType == 2) - { - spriteframe_t rot; - rot.Texture[0] = texnum; - rot.Texture[1] = texnum; - for (int j = 1; j < 8; ++j) - { - rot.Texture[16-j*2] = texnum + j; - rot.Texture[17-j*2] = texnum + j; - } - rot.Flip = 0; - tex->Rotations = SpriteFrames.Push (rot); - } - } -} - -void FTextureManager::AddExtraTextures () -{ - int firsttx = Wads.CheckNumForName ("TX_START"); - int lasttx = Wads.CheckNumForName ("TX_END"); + int firsttx = Wads.CheckNumForName (startlump); + int lasttx = Wads.CheckNumForName (endlump); char name[9]; if (firsttx == -1 || lasttx == -1) @@ -633,14 +280,14 @@ void FTextureManager::AddExtraTextures () { Wads.GetLumpName (name, firsttx); - if (Wads.CheckNumForName (name, ns_newtextures) == firsttx) + if (Wads.CheckNumForName (name, ns) == firsttx) { - CreateTexture (firsttx, FTexture::TEX_Override); + CreateTexture (firsttx, usetype); } } - DefaultTexture = CheckForTexture ("-NOFLAT-", FTexture::TEX_Override, 0); } + void FTextureManager::AddPatches (int lumpnum) { FWadLump *file = Wads.ReopenLumpNum (lumpnum); @@ -663,2412 +310,8 @@ void FTextureManager::AddPatches (int lumpnum) delete file; } -void FTextureManager::AddTexturesLumps (int lump1, int lump2, int patcheslump) -{ - int firstdup = (int)Textures.Size(); - if (lump1 >= 0) - { - FMemLump texdir = Wads.ReadLump (lump1); - AddTexturesLump (texdir.GetMem(), Wads.LumpLength (lump1), patcheslump, firstdup, true); - } - if (lump2 >= 0) - { - FMemLump texdir = Wads.ReadLump (lump2); - AddTexturesLump (texdir.GetMem(), Wads.LumpLength (lump2), patcheslump, firstdup, false); - } -} -void FTextureManager::AddTexturesLump (const void *lumpdata, int lumpsize, int patcheslump, int firstdup, bool texture1) -{ - FPatchLookup *patchlookup; - int i, j; - DWORD numpatches; - - if (firstdup == 0) - { - firstdup = (int)Textures.Size(); - } - - { - FWadLump pnames = Wads.OpenLumpNum (patcheslump); - - pnames >> numpatches; - - // Check whether the amount of names reported is correct. - if (numpatches < 0) - { - I_Error("Corrupt PNAMES lump found (negative amount of entries reported)"); - return; - } - - // Check whether the amount of names reported is correct. - int lumplength = Wads.LumpLength(patcheslump); - if (numpatches > DWORD((lumplength-4)/8)) - { - Printf("PNAMES lump is shorter than required (%ld entries reported but only %d bytes (%d entries) long\n", - numpatches, lumplength, (lumplength-4)/8); - // Truncate but continue reading. Who knows how many such lumps exist? - numpatches = (lumplength-4)/8; - } - - // Catalog the patches these textures use so we know which - // textures they represent. - patchlookup = (FPatchLookup *)alloca (numpatches * sizeof(*patchlookup)); - - for (DWORD i = 0; i < numpatches; ++i) - { - pnames.Read (patchlookup[i].Name, 8); - patchlookup[i].Name[8] = 0; - - j = CheckForTexture (patchlookup[i].Name, FTexture::TEX_WallPatch); - if (j >= 0) - { - patchlookup[i].Texture = Textures[j].Texture; - } - else - { - // Shareware Doom has the same PNAMES lump as the registered - // Doom, so printing warnings for patches that don't really - // exist isn't such a good idea. - //Printf ("Patch %s not found.\n", patchlookup[i].Name); - patchlookup[i].Texture = NULL; - } - } - } - - bool isStrife = false; - const DWORD *maptex, *directory; - DWORD maxoff; - int numtextures; - DWORD offset = 0; // Shut up, GCC! - - maptex = (const DWORD *)lumpdata; - numtextures = LittleLong(*maptex); - maxoff = lumpsize; - - if (maxoff < DWORD(numtextures+1)*4) - { - I_FatalError ("Texture directory is too short"); - } - - // Scan the texture lump to decide if it contains Doom or Strife textures - for (i = 0, directory = maptex+1; i < numtextures; ++i) - { - offset = LittleLong(directory[i]); - if (offset > maxoff) - { - I_FatalError ("Bad texture directory"); - } - - maptexture_t *tex = (maptexture_t *)((BYTE *)maptex + offset); - - // There is bizzarely a Doom editing tool that writes to the - // first two elements of columndirectory, so I can't check those. - if (SAFESHORT(tex->patchcount) <= 0 || - tex->columndirectory[2] != 0 || - tex->columndirectory[3] != 0) - { - isStrife = true; - break; - } - } - - - // Textures defined earlier in the lump take precedence over those defined later, - // but later TEXTUREx lumps take precedence over earlier ones. - for (i = 1, directory = maptex; i <= numtextures; ++i) - { - if (i == 1 && texture1) - { - // The very first texture is just a dummy. Copy its dimensions to texture 0. - // It still needs to be created in case someone uses it by name. - offset = LittleLong(directory[1]); - const maptexture_t *tex = (const maptexture_t *)((const BYTE *)maptex + offset); - FDummyTexture *tex0 = static_cast<FDummyTexture *>(Textures[0].Texture); - tex0->SetSize (SAFESHORT(tex->width), SAFESHORT(tex->height)); - } - - offset = LittleLong(directory[i]); - if (offset > maxoff) - { - I_FatalError ("Bad texture directory"); - } - - // If this texture was defined already in this lump, skip it - // This could cause problems with animations that use the same name for intermediate - // textures. Should I be worried? - for (j = (int)Textures.Size() - 1; j >= firstdup; --j) - { - if (strnicmp (Textures[j].Texture->Name, (const char *)maptex + offset, 8) == 0) - break; - } - if (j + 1 == firstdup) - { - FTexture *tex = new FMultiPatchTexture ((const BYTE *)maptex + offset, patchlookup, numpatches, isStrife); - if (i == 1 && texture1) - { - tex->UseType = FTexture::TEX_Null; - } - AddTexture (tex); - } - } -} - - - - -FTexture::FTexture () -: LeftOffset(0), TopOffset(0), - WidthBits(0), HeightBits(0), ScaleX(8), ScaleY(8), - UseType(TEX_Any), bNoDecals(false), bNoRemap0(false), bWorldPanning(false), - bMasked(true), bAlphaTexture(false), bHasCanvas(false), bWarped(0), - Rotations(0xFFFF), Width(0xFFFF), Height(0), WidthMask(0) -{ - *Name=0; -} - -FTexture::~FTexture () -{ -} - -bool FTexture::CheckModified () -{ - return false; -} - -void FTexture::GetDimensions () -{ - Width = 0; - Height = 0; -} - -void FTexture::SetFrontSkyLayer () -{ - bNoRemap0 = true; -} - -void FTexture::CalcBitSize () -{ - // WidthBits is rounded down, and HeightBits is rounded up - int i; - - for (i = 0; (1 << i) < Width; ++i) - { } - - WidthBits = i; - - // Having WidthBits that would allow for columns past the end of the - // texture is not allowed, even if it means the entire texture is - // not drawn. - if (Width < (1 << WidthBits)) - { - WidthBits--; - } - WidthMask = (1 << WidthBits) - 1; - - // The minimum height is 2, because we cannot shift right 32 bits. - for (i = 1; (1 << i) < Height; ++i) - { } - - HeightBits = i; -} - -FTexture::Span **FTexture::CreateSpans (const BYTE *pixels) const -{ - Span **spans, *span; - - if (!bMasked) - { // Texture does not have holes, so it can use a simpler span structure - spans = (Span **)M_Malloc (sizeof(Span*)*Width + sizeof(Span)*2); - span = (Span *)&spans[Width]; - for (int x = 0; x < Width; ++x) - { - spans[x] = span; - } - span[0].Length = Height; - span[0].TopOffset = 0; - span[1].Length = 0; - span[1].TopOffset = 0; - } - else - { // Texture might have holes, so build a complete span structure - int numcols = Width; - int numrows = Height; - int numspans = numcols; // One span to terminate each column - const BYTE *data_p; - bool newspan; - int x, y; - - data_p = pixels; - - // Count the number of spans in this texture - for (x = numcols; x > 0; --x) - { - newspan = true; - for (y = numrows; y > 0; --y) - { - if (*data_p++ == 0) - { - if (!newspan) - { - newspan = true; - } - } - else if (newspan) - { - newspan = false; - numspans++; - } - } - } - - // Allocate space for the spans - spans = (Span **)M_Malloc (sizeof(Span*)*numcols + sizeof(Span)*numspans); - - // Fill in the spans - for (x = 0, span = (Span *)&spans[numcols], data_p = pixels; x < numcols; ++x) - { - newspan = true; - spans[x] = span; - for (y = 0; y < numrows; ++y) - { - if (*data_p++ == 0) - { - if (!newspan) - { - newspan = true; - span++; - } - } - else - { - if (newspan) - { - newspan = false; - span->TopOffset = y; - span->Length = 1; - } - else - { - span->Length++; - } - } - } - if (!newspan) - { - span++; - } - span->TopOffset = 0; - span->Length = 0; - span++; - } - } - return spans; -} - -void FTexture::FreeSpans (Span **spans) const -{ - free (spans); -} - -void FTexture::CopyToBlock (BYTE *dest, int dwidth, int dheight, int xpos, int ypos, const BYTE *translation) -{ - int x1 = xpos, x2 = x1 + GetWidth(), xo = -x1; - - if (x1 < 0) - { - x1 = 0; - } - if (x2 > dwidth) - { - x2 = dwidth; - } - for (; x1 < x2; ++x1) - { - const BYTE *data; - const Span *span; - BYTE *outtop = &dest[dheight * x1]; - - data = GetColumn (x1 + xo, &span); - - while (span->Length != 0) - { - int len = span->Length; - int y = ypos + span->TopOffset; - int adv = span->TopOffset; - - if (y < 0) - { - adv -= y; - len += y; - y = 0; - } - if (y + len > dheight) - { - len = dheight - y; - } - if (len > 0) - { - if (translation == NULL) - { - memcpy (outtop + y, data + adv, len); - } - else - { - for (int j = 0; j < len; ++j) - { - outtop[y+j] = translation[data[adv+j]]; - } - } - } - span++; - } - } -} - -// Converts a texture between row-major and column-major format -// by flipping it about the X=Y axis. - -void FTexture::FlipSquareBlock (BYTE *block, int x, int y) -{ - int i, j; - - if (x != y) return; - - for (i = 0; i < x; ++i) - { - BYTE *corner = block + x*i + i; - int count = x - i; - if (count & 1) - { - count--; - swap<BYTE> (corner[count], corner[count*x]); - } - for (j = 0; j < count; j += 2) - { - swap<BYTE> (corner[j], corner[j*x]); - swap<BYTE> (corner[j+1], corner[(j+1)*x]); - } - } -} - -void FTexture::FlipSquareBlockRemap (BYTE *block, int x, int y, const BYTE *remap) -{ - int i, j; - BYTE t; - - if (x != y) return; - - for (i = 0; i < x; ++i) - { - BYTE *corner = block + x*i + i; - int count = x - i; - if (count & 1) - { - count--; - t = remap[corner[count]]; - corner[count] = remap[corner[count*x]]; - corner[count*x] = t; - } - for (j = 0; j < count; j += 2) - { - t = remap[corner[j]]; - corner[j] = remap[corner[j*x]]; - corner[j*x] = t; - t = remap[corner[j+1]]; - corner[j+1] = remap[corner[(j+1)*x]]; - corner[(j+1)*x] = t; - } - } -} - -void FTexture::FlipNonSquareBlock (BYTE *dst, const BYTE *src, int x, int y, int srcpitch) -{ - int i, j; - - for (i = 0; i < x; ++i) - { - for (j = 0; j < y; ++j) - { - dst[i*y+j] = src[i+j*srcpitch]; - } - } -} - -void FTexture::FlipNonSquareBlockRemap (BYTE *dst, const BYTE *src, int x, int y, const BYTE *remap) -{ - int i, j; - - for (i = 0; i < x; ++i) - { - for (j = 0; j < y; ++j) - { - dst[i*y+j] = remap[src[i+j*x]]; - } - } -} - -FDummyTexture::FDummyTexture () -{ - Width = 64; - Height = 64; - HeightBits = 6; - WidthBits = 6; - WidthMask = 63; - Name[0] = 0; - UseType = TEX_Null; -} - -void FDummyTexture::Unload () -{ -} - -void FDummyTexture::SetSize (int width, int height) -{ - Width = width; - Height = height; - CalcBitSize (); -} - -// This must never be called -const BYTE *FDummyTexture::GetColumn (unsigned int column, const Span **spans_out) -{ - return NULL; -} - -// And this also must never be called -const BYTE *FDummyTexture::GetPixels () -{ - return NULL; -} - -FPatchTexture::FPatchTexture (int lumpnum, int usetype) -: SourceLump(lumpnum), Pixels(0), Spans(0) -{ - UseType = usetype; - Wads.GetLumpName (Name, lumpnum); - Name[8] = 0; -} - -FPatchTexture::~FPatchTexture () -{ - Unload (); - if (Spans != NULL) - { - FreeSpans (Spans); - Spans = NULL; - } -} - -void FPatchTexture::Unload () -{ - if (Pixels != NULL) - { - delete[] Pixels; - Pixels = NULL; - } -} - -const BYTE *FPatchTexture::GetPixels () -{ - if (Pixels == NULL) - { - MakeTexture (); - } - return Pixels; -} - -const BYTE *FPatchTexture::GetColumn (unsigned int column, const Span **spans_out) -{ - if (Pixels == NULL) - { - MakeTexture (); - } - if ((unsigned)column >= (unsigned)Width) - { - if (WidthMask + 1 == Width) - { - column &= WidthMask; - } - else - { - column %= Width; - } - } - if (spans_out != NULL) - { - *spans_out = Spans[column]; - } - return Pixels + column*Height; -} - -void FPatchTexture::GetDimensions () -{ - // Wads.OpenLumpNum cannot be used here once Zip support has been added. - // To work correctly it needs an assignment operator which - // would cause considerable overhead elsewhere where it isn't needed. - FWadLump * lump = Wads.ReopenLumpNum (SourceLump); - patch_t dummy; - - (*lump) >> dummy.width >> dummy.height; - - if (dummy.width <= 0 || dummy.height <= 0 || dummy.width > 2048 || dummy.height > 2048) - { - delete lump; - lump = Wads.ReopenLumpNum ( Wads.GetNumForName("-BADPATC", ns_graphics) ); - (*lump) >> dummy.width >> dummy.height; - } - - (*lump) >> dummy.leftoffset >> dummy.topoffset; - Width = dummy.width; - Height = dummy.height; - LeftOffset = dummy.leftoffset; - TopOffset = dummy.topoffset; - delete lump; - - CalcBitSize (); -} - -void FPatchTexture::MakeTexture () -{ - BYTE *remap, remaptable[256]; - Span *spanstuffer, *spanstarter; - const column_t *maxcol; - bool warned; - int numspans; - int x; - - FMemLump lump = Wads.ReadLump (SourceLump); - const patch_t *patch = (const patch_t *)lump.GetMem(); - - maxcol = (const column_t *)((const BYTE *)patch + Wads.LumpLength (SourceLump) - 3); - - // Check for badly-sized patches - if (LittleShort(patch->width) <= 0 || LittleShort(patch->height) <= 0) - { - lump = Wads.ReadLump ("-BADPATC"); - patch = (const patch_t *)lump.GetMem(); - Printf (PRINT_BOLD, "Patch %s has a non-positive size.\n", Name); - } - else if (LittleShort(patch->width) > 2048 || LittleShort(patch->height) > 2048) - { - lump = Wads.ReadLump ("-BADPATC"); - patch = (const patch_t *)lump.GetMem(); - Printf (PRINT_BOLD, "Patch %s is too big.\n", Name); - } - - if (Width == 0xFFFF) - { - Width = LittleShort(patch->width); - Height = LittleShort(patch->height); - LeftOffset = LittleShort(patch->leftoffset); - TopOffset = LittleShort(patch->topoffset); - } - CalcBitSize (); - - // Add a little extra space at the end if the texture's height is not - // a power of 2, in case somebody accidentally makes it repeat vertically. - int numpix = Width * Height + (1 << HeightBits) - Height; - - numspans = Width; - - Pixels = new BYTE[numpix]; - memset (Pixels, 0, numpix); - - if (bNoRemap0) - { - memcpy (remaptable, GPalette.Remap, 256); - remaptable[0] = 0; - remap = remaptable; - } - else - { - remap = GPalette.Remap; - } - - // Draw the image to the buffer - for (x = 0; x < Width; ++x) - { - BYTE *outtop = Pixels + x*Height; - const column_t *column = (const column_t *)((const BYTE *)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; - BYTE *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 BYTE *in = (const BYTE *)column + 3; - for (int i = 0; i < len; ++i) - { - out[i] = remap[in[i]]; - } - } - } - column = (const column_t *)((const BYTE *)column + column->length + 4); - } - } - - // Create the spans - if (Spans != NULL) - { - return; - } - - Spans = (Span **)M_Malloc (sizeof(Span*)*Width + sizeof(Span)*numspans); - spanstuffer = (Span *)((BYTE *)Spans + sizeof(Span*)*Width); - warned = false; - - for (x = 0; x < Width; ++x) - { - const column_t *column = (const column_t *)((const BYTE *)patch + LittleLong(patch->columnofs[x])); - int top = -1; - - Spans[x] = spanstuffer; - spanstarter = spanstuffer; - - while (column < maxcol && column->topdelta != 0xFF) - { - if (column->topdelta <= top) - { - top += column->topdelta; - } - else - { - top = column->topdelta; - } - - int len = column->length; - - if (len != 0) - { - if (top + len > Height) // Clip posts that extend past the bottom - { - len = Height - top; - } - if (len > 0) - { - // There is something of this post to draw. If it starts at the same - // place where the previous span ends, add it to that one. If it starts - // before the other one ends, that's bad, but deal with it. If it starts - // after the previous one ends, create another span. - - // Assume we need to create another span. - spanstuffer->TopOffset = top; - spanstuffer->Length = len; - - // Now check if that's really the case. - if (spanstuffer > spanstarter) - { - if ((spanstuffer - 1)->TopOffset + (spanstuffer - 1)->Length == top) - { - (--spanstuffer)->Length += len; - } - else - { - int prevbot; - - while (spanstuffer > spanstarter && - spanstuffer->TopOffset < (prevbot = - (spanstuffer - 1)->TopOffset + (spanstuffer - 1)->Length)) - { - if (spanstuffer->TopOffset < (spanstuffer - 1)->TopOffset) - { - (spanstuffer - 1)->TopOffset = spanstuffer->TopOffset; - } - (spanstuffer - 1)->Length = MAX(prevbot, - spanstuffer->TopOffset + spanstuffer->Length) - - (spanstuffer - 1)->TopOffset; - spanstuffer--; - if (!warned) - { - warned = true; - Printf (PRINT_BOLD, "Patch %s is malformed.\n", Name); - } - } - } - } - spanstuffer++; - } - } - column = (const column_t *)((const BYTE *)column + column->length + 4); - } - - spanstuffer->Length = spanstuffer->TopOffset = 0; - spanstuffer++; - } -} - -// Fix for certain special patches on single-patch textures. -void FPatchTexture::HackHack (int newheight) -{ - BYTE *out; - int x; - - Unload (); - if (Spans != NULL) - { - FreeSpans (Spans); - } - - { - FMemLump lump = Wads.ReadLump (SourceLump); - const patch_t *patch = (const patch_t *)lump.GetMem(); - - Width = LittleShort(patch->width); - Height = newheight; - LeftOffset = 0; - TopOffset = 0; - - Pixels = new BYTE[Width * Height]; - - // Draw the image to the buffer - for (x = 0, out = Pixels; x < Width; ++x) - { - const BYTE *in = (const BYTE *)patch + LittleLong(patch->columnofs[x]) + 3; - - for (int y = newheight; y > 0; --y) - { - *out = *in != 255 ? *in : Near255; - out++, in++; - } - out += newheight; - } - } - - // Create the spans - Spans = (Span **)M_Malloc (sizeof(Span *)*Width + sizeof(Span)*Width*2); - - Span *span = (Span *)&Spans[Width]; - - for (x = 0; x < Width; ++x) - { - Spans[x] = span; - span[0].Length = newheight; - span[0].TopOffset = 0; - span[1].Length = 0; - span[1].TopOffset = 0; - span += 2; - } -} - -FFlatTexture::FFlatTexture (int lumpnum) -: SourceLump(lumpnum), Pixels(0) -{ - int area; - int bits; - - Wads.GetLumpName (Name, lumpnum); - Name[8] = 0; - area = Wads.LumpLength (lumpnum); - - switch (area) - { - default: - case 64*64: bits = 6; break; - case 8*8: bits = 3; break; - case 16*16: bits = 4; break; - case 32*32: bits = 5; break; - case 128*128: bits = 7; break; - case 256*256: bits = 8; break; - } - - bMasked = false; - WidthBits = HeightBits = bits; - Width = Height = 1 << bits; - WidthMask = (1 << bits) - 1; - DummySpans[0].TopOffset = 0; - DummySpans[0].Length = Height; - DummySpans[1].TopOffset = 0; - DummySpans[1].Length = 0; - - if (bits > 6) - { - ScaleX = ScaleY = 8 << (bits - 6); - } - else - { - ScaleX = ScaleY = 8; - } - - UseType = TEX_Flat; -} - -FFlatTexture::~FFlatTexture () -{ - Unload (); -} - -void FFlatTexture::Unload () -{ - if (Pixels != NULL) - { - delete[] Pixels; - Pixels = NULL; - } -} - -const BYTE *FFlatTexture::GetColumn (unsigned int column, const Span **spans_out) -{ - if (Pixels == NULL) - { - MakeTexture (); - } - if ((unsigned)column >= (unsigned)Width) - { - if (WidthMask + 1 == Width) - { - column &= WidthMask; - } - else - { - column %= Width; - } - } - if (spans_out != NULL) - { - *spans_out = DummySpans; - } - return Pixels + column*Height; -} - -const BYTE *FFlatTexture::GetPixels () -{ - if (Pixels == NULL) - { - MakeTexture (); - } - return Pixels; -} - -void FFlatTexture::MakeTexture () -{ - FWadLump lump = Wads.OpenLumpNum (SourceLump); - Pixels = new BYTE[Width*Height]; - long numread = lump.Read (Pixels, Width*Height); - if (numread < Width*Height) - { - memset (Pixels + numread, 0xBB, Width*Height - numread); - } - FlipSquareBlockRemap (Pixels, Width, Height, GPalette.Remap); -} - -const FTexture::Span FRawPageTexture::DummySpans[2] = -{ - { 0, 200 }, { 0, 0 } -}; - -FRawPageTexture::FRawPageTexture (int lumpnum) -: SourceLump(lumpnum), Pixels(0) -{ - Wads.GetLumpName (Name, lumpnum); - Name[8] = 0; - - Width = 320; - Height = 200; - WidthBits = 8; - HeightBits = 8; - WidthMask = 255; - - UseType = TEX_MiscPatch; -} - -FRawPageTexture::~FRawPageTexture () -{ - Unload (); -} - -void FRawPageTexture::Unload () -{ - if (Pixels != NULL) - { - delete[] Pixels; - Pixels = NULL; - } -} - -const BYTE *FRawPageTexture::GetColumn (unsigned int column, const Span **spans_out) -{ - if (Pixels == NULL) - { - MakeTexture (); - } - if ((unsigned)column >= (unsigned)Width) - { - column %= 320; - } - if (spans_out != NULL) - { - *spans_out = DummySpans; - } - return Pixels + column*Height; -} - -const BYTE *FRawPageTexture::GetPixels () -{ - if (Pixels == NULL) - { - MakeTexture (); - } - return Pixels; -} - -void FRawPageTexture::MakeTexture () -{ - FMemLump lump = Wads.ReadLump (SourceLump); - const BYTE *source = (const BYTE *)lump.GetMem(); - const BYTE *source_p = source; - BYTE *dest_p; - - Pixels = new BYTE[Width*Height]; - dest_p = Pixels; - - // Convert the source image from row-major to column-major format - for (int y = 200; y != 0; --y) - { - for (int x = 320; x != 0; --x) - { - *dest_p = GPalette.Remap[*source_p]; - dest_p += 200; - source_p++; - } - dest_p -= 200*320-1; - } -} - -FIMGZTexture::FIMGZTexture (int lumpnum) -: SourceLump(lumpnum), Pixels(0), Spans(0) -{ - Wads.GetLumpName (Name, lumpnum); - Name[8] = 0; - - UseType = TEX_MiscPatch; -} - -FIMGZTexture::~FIMGZTexture () -{ - Unload (); - if (Spans != NULL) - { - FreeSpans (Spans); - Spans = NULL; - } -} - -void FIMGZTexture::Unload () -{ - if (Pixels != NULL) - { - delete[] Pixels; - Pixels = NULL; - } -} - -const BYTE *FIMGZTexture::GetColumn (unsigned int column, const Span **spans_out) -{ - if (Pixels == NULL) - { - MakeTexture (); - } - if ((unsigned)column >= (unsigned)Width) - { - if (WidthMask + 1 == Width) - { - column &= WidthMask; - } - else - { - column %= Width; - } - } - if (spans_out != NULL) - { - *spans_out = Spans[column]; - } - return Pixels + column*Height; -} - -const BYTE *FIMGZTexture::GetPixels () -{ - if (Pixels == NULL) - { - MakeTexture (); - } - return Pixels; -} - -void FIMGZTexture::GetDimensions () -{ - FWadLump lump = Wads.OpenLumpNum (SourceLump); - DWORD magic; - WORD w, h; - SWORD l, t; - - lump >> magic >> w >> h >> l >> t; - - Width = w; - Height = h; - LeftOffset = l; - TopOffset = t; - CalcBitSize (); -} - -void FIMGZTexture::MakeTexture () -{ - FMemLump lump = Wads.ReadLump (SourceLump); - const ImageHeader *imgz = (const ImageHeader *)lump.GetMem(); - const BYTE *data = (const BYTE *)&imgz[1]; - - if (Width != 0xFFFF) - { - Width = LittleShort(imgz->Width); - Height = LittleShort(imgz->Height); - LeftOffset = LittleShort(imgz->LeftOffset); - TopOffset = LittleShort(imgz->TopOffset); - } - - BYTE *dest_p; - int dest_adv = Height; - int dest_rew = Width * Height - 1; - - CalcBitSize (); - Pixels = new BYTE[Width*Height]; - dest_p = Pixels; - - // Convert the source image from row-major to column-major format - if (!imgz->Compression) - { - for (int y = Height; y != 0; --y) - { - for (int x = Width; x != 0; --x) - { - *dest_p = *data; - dest_p += dest_adv; - data++; - } - dest_p -= dest_rew; - } - } - else - { - // IMGZ compression is the same RLE used by IFF ILBM files - int runlen = 0, setlen = 0; - BYTE setval = 0; // Shut up, GCC - - for (int y = Height; y != 0; --y) - { - for (int x = Width; x != 0; ) - { - if (runlen != 0) - { - BYTE color = *data; - *dest_p = color; - dest_p += dest_adv; - data++; - x--; - runlen--; - } - else if (setlen != 0) - { - *dest_p = setval; - dest_p += dest_adv; - x--; - setlen--; - } - else - { - SBYTE code = *data++; - if (code >= 0) - { - runlen = code + 1; - } - else if (code != -128) - { - setlen = (-code) + 1; - setval = *data++; - } - } - } - dest_p -= dest_rew; - } - } - - if (Spans == NULL) - { - Spans = CreateSpans (Pixels); - } -} - - -static BYTE GrayMap[256]; - -static void InitGrayMap() -{ - if (GrayMap[0] == GrayMap[255]) - { - for (int i = 0; i < 256; ++i) - { - GrayMap[i] = ColorMatcher.Pick (i, i, i); - } - } -} - -FPNGTexture::FPNGTexture (int lumpnum, int width, int height, - BYTE depth, BYTE colortype, BYTE interlace) -: SourceLump(lumpnum), Pixels(0), Spans(0), - BitDepth(depth), ColorType(colortype), Interlace(interlace), - PaletteMap(0), PaletteSize(0), StartOfIDAT(0) -{ - union - { - DWORD palette[256]; - BYTE pngpal[256][3]; - }; - BYTE trans[256]; - bool havetRNS = false; - DWORD len, id; - int i; - - Wads.GetLumpName (Name, lumpnum); - Name[8] = 0; - - UseType = TEX_MiscPatch; - LeftOffset = 0; - TopOffset = 0; - bMasked = false; - - Width = width; - Height = height; - CalcBitSize (); - - memset (trans, 255, 256); - - // Parse pre-IDAT chunks. I skip the CRCs. Is that bad? - FWadLump lump = Wads.OpenLumpNum (SourceLump); - lump.Seek (33, SEEK_SET); - - lump >> len >> id; - while (id != MAKE_ID('I','D','A','T') && id != MAKE_ID('I','E','N','D')) - { - len = BigLong((unsigned int)len); - switch (id) - { - default: - lump.Seek (len, SEEK_CUR); - break; - - case MAKE_ID('g','r','A','b'): - // This is like GRAB found in an ILBM, except coordinates use 4 bytes - { - DWORD hotx, hoty; - - lump >> hotx >> hoty; - LeftOffset = BigLong((int)hotx); - TopOffset = BigLong((int)hoty); - } - break; - - case MAKE_ID('P','L','T','E'): - PaletteSize = MIN<int> (len / 3, 256); - lump.Read (pngpal, PaletteSize * 3); - if (PaletteSize * 3 != (int)len) - { - lump.Seek (len - PaletteSize * 3, SEEK_CUR); - } - for (i = PaletteSize - 1; i >= 0; --i) - { - palette[i] = MAKERGB(pngpal[i][0], pngpal[i][1], pngpal[i][2]); - } - break; - - case MAKE_ID('t','R','N','S'): - lump.Read (trans, len); - havetRNS = true; - break; - - case MAKE_ID('a','l','P','h'): - bAlphaTexture = true; - bMasked = true; - break; - } - lump >> len >> len; // Skip CRC - id = MAKE_ID('I','E','N','D'); - lump >> id; - } - StartOfIDAT = lump.Tell() - 8; - - switch (colortype) - { - case 4: // Grayscale + Alpha - bMasked = true; - // intentional fall-through - - case 0: // Grayscale - if (!bAlphaTexture) - { - InitGrayMap(); - if (colortype == 0 && havetRNS && trans[0] != 0) - { - bMasked = true; - PaletteSize = 256; - PaletteMap = new BYTE[256]; - memcpy (PaletteMap, GrayMap, 256); - PaletteMap[trans[0]] = 0; - } - else - { - PaletteMap = GrayMap; - } - } - break; - - case 3: // Paletted - PaletteMap = new BYTE[PaletteSize]; - GPalette.MakeRemap (palette, PaletteMap, trans, PaletteSize); - for (i = 0; i < PaletteSize; ++i) - { - if (trans[i] == 0) - { - bMasked = true; - PaletteMap[i] = 0; - } - } - break; - - case 6: // RGB + Alpha - bMasked = true; - break; - } -} - -FPNGTexture::~FPNGTexture () -{ - Unload (); - if (Spans != NULL) - { - FreeSpans (Spans); - Spans = NULL; - } - if (PaletteMap != NULL && PaletteMap != GrayMap) - { - delete[] PaletteMap; - PaletteMap = NULL; - } -} - -void FPNGTexture::Unload () -{ - if (Pixels != NULL) - { - delete[] Pixels; - Pixels = NULL; - } -} - -const BYTE *FPNGTexture::GetColumn (unsigned int column, const Span **spans_out) -{ - if (Pixels == NULL) - { - MakeTexture (); - } - if ((unsigned)column >= (unsigned)Width) - { - if (WidthMask + 1 == Width) - { - column &= WidthMask; - } - else - { - column %= Width; - } - } - if (spans_out != NULL) - { - *spans_out = Spans[column]; - } - return Pixels + column*Height; -} - -const BYTE *FPNGTexture::GetPixels () -{ - if (Pixels == NULL) - { - MakeTexture (); - } - return Pixels; -} - -void FPNGTexture::MakeTexture () -{ - FWadLump lump = Wads.OpenLumpNum (SourceLump); - - Pixels = new BYTE[Width*Height]; - if (StartOfIDAT == 0) - { - memset (Pixels, 0x99, Width*Height); - } - else - { - DWORD len, id; - lump.Seek (StartOfIDAT, SEEK_SET); - lump >> len >> id; - - if (ColorType == 0 || ColorType == 3) /* Grayscale and paletted */ - { - M_ReadIDAT (&lump, Pixels, Width, Height, Width, BitDepth, ColorType, Interlace, BigLong((unsigned int)len)); - - if (Width == Height) - { - if (PaletteMap != NULL) - { - FlipSquareBlockRemap (Pixels, Width, Height, PaletteMap); - } - else - { - FlipSquareBlock (Pixels, Width, Height); - } - } - else - { - BYTE *newpix = new BYTE[Width*Height]; - if (PaletteMap != NULL) - { - FlipNonSquareBlockRemap (newpix, Pixels, Width, Height, PaletteMap); - } - else - { - FlipNonSquareBlock (newpix, Pixels, Width, Height, Width); - } - BYTE *oldpix = Pixels; - Pixels = newpix; - delete[] oldpix; - } - } - else /* RGB and/or Alpha present */ - { - int bytesPerPixel = ColorType == 2 ? 3 : ColorType == 4 ? 2 : 4; - BYTE *tempix = new BYTE[Width * Height * bytesPerPixel]; - BYTE *in, *out; - int x, y, pitch, backstep; - - M_ReadIDAT (&lump, tempix, Width, Height, Width*bytesPerPixel, BitDepth, ColorType, Interlace, BigLong((unsigned int)len)); - in = tempix; - out = Pixels; - - // Convert from source format to paletted, column-major. - // Formats with alpha maps are reduced to only 1 bit of alpha. - switch (ColorType) - { - case 2: // RGB - pitch = Width * 3; - backstep = Height * pitch - 3; - for (x = Width; x > 0; --x) - { - for (y = Height; y > 0; --y) - { - *out++ = RGB32k[in[0]>>3][in[1]>>3][in[2]>>3]; - in += pitch; - } - in -= backstep; - } - break; - - case 4: // Grayscale + Alpha - pitch = Width * 2; - backstep = Height * pitch - 2; - if (PaletteMap != NULL) - { - for (x = Width; x > 0; --x) - { - for (y = Height; y > 0; --y) - { - *out++ = in[1] < 128 ? 0 : PaletteMap[in[0]]; - in += pitch; - } - in -= backstep; - } - } - else - { - for (x = Width; x > 0; --x) - { - for (y = Height; y > 0; --y) - { - *out++ = in[1] < 128 ? 0 : in[0]; - in += pitch; - } - in -= backstep; - } - } - break; - - case 6: // RGB + Alpha - pitch = Width * 4; - backstep = Height * pitch - 4; - for (x = Width; x > 0; --x) - { - for (y = Height; y > 0; --y) - { - *out++ = in[3] < 128 ? 0 : RGB32k[in[0]>>3][in[1]>>3][in[2]>>3]; - in += pitch; - } - in -= backstep; - } - break; - } - delete[] tempix; - } - } - if (Spans == NULL) - { - Spans = CreateSpans (Pixels); - } -} - -FLumpSourceMgr::FLumpSourceMgr (FileReader *lump, j_decompress_ptr cinfo) -: Lump (lump) -{ - cinfo->src = this; - init_source = InitSource; - fill_input_buffer = FillInputBuffer; - skip_input_data = SkipInputData; - resync_to_restart = jpeg_resync_to_restart; - term_source = TermSource; - bytes_in_buffer = 0; - next_input_byte = NULL; -} - -void FLumpSourceMgr::InitSource (j_decompress_ptr cinfo) -{ - ((FLumpSourceMgr *)(cinfo->src))->StartOfFile = true; -} - -boolean FLumpSourceMgr::FillInputBuffer (j_decompress_ptr cinfo) -{ - FLumpSourceMgr *me = (FLumpSourceMgr *)(cinfo->src); - long nbytes = me->Lump->Read (me->Buffer, sizeof(me->Buffer)); - - if (nbytes <= 0) - { - me->Buffer[0] = (JOCTET)0xFF; - me->Buffer[1] = (JOCTET)JPEG_EOI; - nbytes = 2; - } - me->next_input_byte = me->Buffer; - me->bytes_in_buffer = nbytes; - me->StartOfFile = false; - return TRUE; -} - -void FLumpSourceMgr::SkipInputData (j_decompress_ptr cinfo, long num_bytes) -{ - FLumpSourceMgr *me = (FLumpSourceMgr *)(cinfo->src); - if (num_bytes <= (long)me->bytes_in_buffer) - { - me->bytes_in_buffer -= num_bytes; - me->next_input_byte += num_bytes; - } - else - { - num_bytes -= (long)me->bytes_in_buffer; - me->Lump->Seek (num_bytes, SEEK_CUR); - FillInputBuffer (cinfo); - } -} - -void FLumpSourceMgr::TermSource (j_decompress_ptr cinfo) -{ -} - -void JPEG_ErrorExit (j_common_ptr cinfo) -{ - (*cinfo->err->output_message) (cinfo); - throw -1; -} - -void JPEG_OutputMessage (j_common_ptr cinfo) -{ - char buffer[JMSG_LENGTH_MAX]; - - (*cinfo->err->format_message) (cinfo, buffer); - Printf (TEXTCOLOR_ORANGE "JPEG failure: %s\n", buffer); -} - -FJPEGTexture::FJPEGTexture (int lumpnum, int width, int height) -: SourceLump(lumpnum), Pixels(0) -{ - Wads.GetLumpName (Name, lumpnum); - Name[8] = 0; - - UseType = TEX_MiscPatch; - LeftOffset = 0; - TopOffset = 0; - bMasked = false; - - Width = width; - Height = height; - CalcBitSize (); - - DummySpans[0].TopOffset = 0; - DummySpans[0].Length = Height; - DummySpans[1].TopOffset = 0; - DummySpans[1].Length = 0; -} - -FJPEGTexture::~FJPEGTexture () -{ - Unload (); -} - -void FJPEGTexture::Unload () -{ - if (Pixels != NULL) - { - delete[] Pixels; - Pixels = NULL; - } -} - -const BYTE *FJPEGTexture::GetColumn (unsigned int column, const Span **spans_out) -{ - if (Pixels == NULL) - { - MakeTexture (); - } - if ((unsigned)column >= (unsigned)Width) - { - if (WidthMask + 1 == Width) - { - column &= WidthMask; - } - else - { - column %= Width; - } - } - if (spans_out != NULL) - { - *spans_out = DummySpans; - } - return Pixels + column*Height; -} - -const BYTE *FJPEGTexture::GetPixels () -{ - if (Pixels == NULL) - { - MakeTexture (); - } - return Pixels; -} - -void FJPEGTexture::MakeTexture () -{ - FWadLump lump = Wads.OpenLumpNum (SourceLump); - JSAMPLE *buff = NULL; - - jpeg_decompress_struct cinfo; - jpeg_error_mgr jerr; - - Pixels = new BYTE[Width * Height]; - memset (Pixels, 0xBA, Width * Height); - - cinfo.err = jpeg_std_error(&jerr); - cinfo.err->output_message = JPEG_OutputMessage; - cinfo.err->error_exit = JPEG_ErrorExit; - jpeg_create_decompress(&cinfo); - try - { - FLumpSourceMgr sourcemgr(&lump, &cinfo); - jpeg_read_header(&cinfo, TRUE); - if (!((cinfo.out_color_space == JCS_RGB && cinfo.num_components == 3) || - (cinfo.out_color_space == JCS_CMYK && cinfo.num_components == 4) || - (cinfo.out_color_space == JCS_GRAYSCALE && cinfo.num_components == 1))) - { - Printf (TEXTCOLOR_ORANGE "Unsupported color format\n", Name); - throw -1; - } - if (cinfo.out_color_space == JCS_GRAYSCALE) - { - InitGrayMap(); - } - - jpeg_start_decompress(&cinfo); - - int y = 0; - buff = new BYTE[cinfo.output_width * cinfo.output_components]; - - while (cinfo.output_scanline < cinfo.output_height) - { - int num_scanlines = jpeg_read_scanlines(&cinfo, &buff, 1); - BYTE *in = buff; - BYTE *out = Pixels + y; - switch (cinfo.out_color_space) - { - case JCS_RGB: - for (int x = Width; x > 0; --x) - { - *out = RGB32k[in[0]>>3][in[1]>>3][in[2]>>3]; - out += Height; - in += 3; - } - break; - - case JCS_GRAYSCALE: - for (int x = Width; x > 0; --x) - { - *out = GrayMap[in[0]]; - out += Height; - in += 1; - } - break; - - case JCS_CMYK: - // What are you doing using a CMYK image? :) - for (int x = Width; x > 0; --x) - { - // To be precise, these calculations should use 255, but - // 256 is much faster and virtually indistinguishable. - int r = in[3] - (((256-in[0])*in[3]) >> 8); - int g = in[3] - (((256-in[1])*in[3]) >> 8); - int b = in[3] - (((256-in[2])*in[3]) >> 8); - *out = RGB32k[r >> 3][g >> 3][b >> 3]; - out += Height; - in += 4; - } - break; - } - y++; - } - jpeg_finish_decompress(&cinfo); - jpeg_destroy_decompress(&cinfo); - } - catch (int) - { - Printf (TEXTCOLOR_ORANGE " in texture %s\n", Name); - jpeg_destroy_decompress(&cinfo); - } - if (buff != NULL) - { - delete[] buff; - } -} - -FBuildTexture::FBuildTexture (int tilenum, const BYTE *pixels, int width, int height, int left, int top) -: Pixels (pixels), Spans (NULL) -{ - Width = width; - Height = height; - LeftOffset = left; - TopOffset = top; - CalcBitSize (); - sprintf (Name, "BTIL%04d", tilenum); - UseType = TEX_Build; -} - -FBuildTexture::~FBuildTexture () -{ - if (Spans != NULL) - { - FreeSpans (Spans); - Spans = NULL; - } -} - -void FBuildTexture::Unload () -{ - // Nothing to do, since the pixels are accessed from memory-mapped files directly -} - -const BYTE *FBuildTexture::GetPixels () -{ - return Pixels; -} - -const BYTE *FBuildTexture::GetColumn (unsigned int column, const Span **spans_out) -{ - if (column >= Width) - { - if (WidthMask + 1 == Width) - { - column &= WidthMask; - } - else - { - column %= Width; - } - } - if (spans_out != NULL) - { - if (Spans == NULL) - { - Spans = CreateSpans (Pixels); - } - *spans_out = Spans[column]; - } - return Pixels + column*Height; -} - -FMultiPatchTexture::FMultiPatchTexture (const void *texdef, FPatchLookup *patchlookup, int maxpatchnum, bool strife) -: Pixels (0), Spans(0), Parts(0), bRedirect(false) -{ - union - { - const maptexture_t *d; - const strifemaptexture_t *s; - } - mtexture; - - union - { - const mappatch_t *d; - const strifemappatch_t *s; - } - mpatch; - - int i; - - mtexture.d = (const maptexture_t *)texdef; - - if (strife) - { - NumParts = SAFESHORT(mtexture.s->patchcount); - } - else - { - NumParts = SAFESHORT(mtexture.d->patchcount); - } - - if (NumParts <= 0) - { - I_FatalError ("Bad texture directory"); - } - - UseType = FTexture::TEX_Wall; - Parts = new TexPart[NumParts]; - Width = SAFESHORT(mtexture.d->width); - Height = SAFESHORT(mtexture.d->height); - strncpy (Name, mtexture.d->name, 8); - Name[8] = 0; - - CalcBitSize (); - - // [RH] Special for beta 29: Values of 0 will use the tx/ty cvars - // to determine scaling instead of defaulting to 8. I will likely - // remove this once I finish the betas, because by then, users - // should be able to actually create scaled textures. - // 10-June-2003: It's still here long after beta 29. Heh. - - ScaleX = mtexture.d->ScaleX ? mtexture.d->ScaleX : 0; - ScaleY = mtexture.d->ScaleY ? mtexture.d->ScaleY : 0; - - if (mtexture.d->Flags & MAPTEXF_WORLDPANNING) - { - bWorldPanning = true; - } - - if (strife) - { - mpatch.s = &mtexture.s->patches[0]; - } - else - { - mpatch.d = &mtexture.d->patches[0]; - } - - for (i = 0; i < NumParts; ++i) - { - if (unsigned(LittleShort(mpatch.d->patch)) >= unsigned(maxpatchnum)) - { - I_FatalError ("Bad PNAMES and/or texture directory:\n\nPNAMES has %d entries, but\n%s wants to use entry %d.", - maxpatchnum, Name, LittleShort(mpatch.d->patch)+1); - } - Parts[i].OriginX = LittleShort(mpatch.d->originx); - Parts[i].OriginY = LittleShort(mpatch.d->originy); - Parts[i].Texture = patchlookup[LittleShort(mpatch.d->patch)].Texture; - if (Parts[i].Texture == NULL) - { - Printf ("Unknown patch %s in texture %s\n", patchlookup[LittleShort(mpatch.d->patch)].Name, Name); - NumParts--; - i--; - } - if (strife) - mpatch.s++; - else - mpatch.d++; - } - if (NumParts == 0) - { - Printf ("Texture %s is left without any patches\n", Name); - } - - CheckForHacks (); - - // If this texture is just a wrapper around a single patch, we can simply - // forward GetPixels() and GetColumn() calls to that patch. - if (NumParts == 1) - { - if (Parts->OriginX == 0 && Parts->OriginY == 0 && - Parts->Texture->GetWidth() == Width && - Parts->Texture->GetHeight() == Height) - { - bRedirect = true; - } - } -} - -FMultiPatchTexture::~FMultiPatchTexture () -{ - Unload (); - if (Parts != NULL) - { - delete[] Parts; - Parts = NULL; - } - if (Spans != NULL) - { - FreeSpans (Spans); - Spans = NULL; - } -} - -void FMultiPatchTexture::SetFrontSkyLayer () -{ - for (int i = 0; i < NumParts; ++i) - { - Parts[i].Texture->SetFrontSkyLayer (); - } - bNoRemap0 = true; -} - -void FMultiPatchTexture::Unload () -{ - if (Pixels != NULL) - { - delete[] Pixels; - Pixels = NULL; - } -} - -const BYTE *FMultiPatchTexture::GetPixels () -{ - if (bRedirect) - { - return Parts->Texture->GetPixels (); - } - if (Pixels == NULL) - { - MakeTexture (); - } - return Pixels; -} - -const BYTE *FMultiPatchTexture::GetColumn (unsigned int column, const Span **spans_out) -{ - if (bRedirect) - { - return Parts->Texture->GetColumn (column, spans_out); - } - if (Pixels == NULL) - { - MakeTexture (); - } - if ((unsigned)column >= (unsigned)Width) - { - if (WidthMask + 1 == Width) - { - column &= WidthMask; - } - else - { - column %= Width; - } - } - if (spans_out != NULL) - { - *spans_out = Spans[column]; - } - return Pixels + column*Height; -} - -void FMultiPatchTexture::MakeTexture () -{ - // Add a little extra space at the end if the texture's height is not - // a power of 2, in case somebody accidentally makes it repeat vertically. - int numpix = Width * Height + (1 << HeightBits) - Height; - - Pixels = new BYTE[numpix]; - memset (Pixels, 0, numpix); - - for (int i = 0; i < NumParts; ++i) - { - Parts[i].Texture->CopyToBlock (Pixels, Width, Height, - Parts[i].OriginX, Parts[i].OriginY); - } - - if (Spans == NULL) - { - Spans = CreateSpans (Pixels); - } -} - -void FMultiPatchTexture::CheckForHacks () -{ - if (NumParts <= 0) - { - return; - } - - // Heretic sky textures are marked as only 128 pixels tall, - // even though they are really 200 pixels tall. - if (gameinfo.gametype == GAME_Heretic && - Name[0] == 'S' && - Name[1] == 'K' && - Name[2] == 'Y' && - Name[4] == 0 && - Name[3] >= '1' && - Name[3] <= '3' && - Height == 128) - { - Height = 200; - HeightBits = 8; - return; - } - - // The Doom E1 sky has its patch's y offset at -8 instead of 0. - if (gameinfo.gametype == GAME_Doom && - !(gameinfo.flags & GI_MAPxx) && - NumParts == 1 && - Height == 128 && - Parts->OriginY == -8 && - Name[0] == 'S' && - Name[1] == 'K' && - Name[2] == 'Y' && - Name[3] == '1' && - Name[4] == 0) - { - Parts->OriginY = 0; - return; - } - - // BIGDOOR7 in Doom also has patches at y offset -4 instead of 0. - if (gameinfo.gametype == GAME_Doom && - !(gameinfo.flags & GI_MAPxx) && - NumParts == 2 && - Height == 128 && - Parts[0].OriginY == -4 && - Parts[1].OriginY == -4 && - Name[0] == 'B' && - Name[1] == 'I' && - Name[2] == 'G' && - Name[3] == 'D' && - Name[4] == 'O' && - Name[5] == 'O' && - Name[6] == 'R' && - Name[7] == '7') - { - Parts[0].OriginY = 0; - Parts[1].OriginY = 0; - return; - } - - // [RH] Some wads (I forget which!) have single-patch textures 256 - // pixels tall that have patch lengths recorded as 0. I can't think of - // any good reason for them to do this, and since I didn't make note - // of which wad made me hack in support for them, the hack is gone - // because I've added support for DeePsea's true tall patches. - // - // Okay, I found a wad with crap patches: Pleiades.wad's sky patches almost - // fit this description and are a big mess, but they're not single patch! - if (Height == 256) - { - int i; - - // All patches must be at the top of the texture for this fix - for (i = 0; i < NumParts; ++i) - { - if (Parts[i].OriginX != 0) - { - break; - } - } - - if (i == NumParts) - { - for (i = 0; i < NumParts; ++i) - { - FPatchTexture *tex = (FPatchTexture *)Parts[i].Texture; - // 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 (tex->SourceLump); - const patch_t *realpatch = (patch_t *)lump.GetMem(); - const DWORD *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*)((byte*)realpatch+LittleLong(cofs[x])); - if (col->topdelta != 0 || col->length != 0) - { - break; // It's not bad! - } - col = (column_t *)((byte *)col + 256 + 4); - if (col->topdelta != 0xFF) - { - break; // More than one post in a column! - } - } - if (x == x2) - { // If all the columns were checked, it needs fixing. - tex->HackHack (Height); - } - } - } - } - } -} - - -FWarpTexture::FWarpTexture (FTexture *source) -: SourcePic (source), Pixels (0), Spans (0), GenTime (0) -{ - Width = source->GetWidth (); - Height = source->GetHeight (); - LeftOffset = source->LeftOffset; - TopOffset = source->TopOffset; - WidthBits = source->WidthBits; - HeightBits = source->HeightBits; - WidthMask = (1 << WidthBits) - 1; - ScaleX = source->ScaleX; - ScaleY = source->ScaleY; - bNoDecals = source->bNoDecals; - Rotations = source->Rotations; - bWarped = 1; -} - -FWarpTexture::~FWarpTexture () -{ - Unload (); - if (Spans != NULL) - { - FreeSpans (Spans); - Spans = NULL; - } - delete SourcePic; -} - -void FWarpTexture::Unload () -{ - if (Pixels != NULL) - { - delete[] Pixels; - Pixels = NULL; - } - if (Spans != NULL) - { - FreeSpans (Spans); - Spans = NULL; - } - SourcePic->Unload (); -} - -bool FWarpTexture::CheckModified () -{ - return r_FrameTime != GenTime; -} - -const BYTE *FWarpTexture::GetPixels () -{ - DWORD time = r_FrameTime; - - if (Pixels == NULL || time != GenTime) - { - MakeTexture (time); - } - return Pixels; -} - -const BYTE *FWarpTexture::GetColumn (unsigned int column, const Span **spans_out) -{ - DWORD time = r_FrameTime; - - if (Pixels == NULL || time != GenTime) - { - MakeTexture (time); - } - if ((unsigned)column >= (unsigned)Width) - { - if (WidthMask + 1 == Width) - { - column &= WidthMask; - } - else - { - column %= Width; - } - } - if (spans_out != NULL) - { - if (Spans == NULL) - { - Spans = CreateSpans (Pixels); - } - *spans_out = Spans[column]; - } - return Pixels + column*Height; -} - -void FWarpTexture::MakeTexture (DWORD time) -{ - const BYTE *otherpix = SourcePic->GetPixels (); - - if (Pixels == NULL) - { - Pixels = new BYTE[Width * Height]; - } - if (Spans != NULL) - { - FreeSpans (Spans); - Spans = NULL; - } - - GenTime = time; - - byte *buffer = (byte *)alloca (MAX (Width, Height)); - int xsize = Width; - int ysize = Height; - int xmask = WidthMask; - int ymask = Height - 1; - int ybits = HeightBits; - int x, y; - - if ((1 << ybits) > Height) - { - ybits--; - } - - DWORD timebase = time * 32 / 28; - for (y = ysize-1; y >= 0; y--) - { - int xt, xf = (finesine[(timebase+y*128)&FINEMASK]>>13) & xmask; - const BYTE *source = otherpix + y; - BYTE *dest = Pixels + y; - for (xt = xsize; xt; xt--, xf = (xf+1)&xmask, dest += ysize) - *dest = source[xf << ybits]; - } - timebase = time * 23 / 28; - for (x = xsize-1; x >= 0; x--) - { - int yt, yf = (finesine[(time+(x+17)*128)&FINEMASK]>>13) & ymask; - const BYTE *source = Pixels + (x << ybits); - BYTE *dest = buffer; - for (yt = ysize; yt; yt--, yf = (yf+1)&ymask) - *dest++ = source[yf]; - memcpy (Pixels+(x<<ybits), buffer, ysize); - } -} - -// [GRB] Eternity-like warping -FWarp2Texture::FWarp2Texture (FTexture *source) -: FWarpTexture (source) -{ - bWarped = 2; -} - -void FWarp2Texture::MakeTexture (DWORD time) -{ - const BYTE *otherpix = SourcePic->GetPixels (); - - if (Pixels == NULL) - { - Pixels = new BYTE[Width * Height]; - } - if (Spans != NULL) - { - FreeSpans (Spans); - Spans = NULL; - } - - GenTime = time; - - int xsize = Width; - int ysize = Height; - int xmask = WidthMask; - int ymask = Height - 1; - int ybits = HeightBits; - int x, y; - - if ((1 << ybits) > Height) - { - ybits--; - } - - DWORD timebase = time * 40 / 28; - for (x = xsize-1; x >= 0; x--) - { - for (y = ysize-1; y >= 0; y--) - { - int xt = (x + 128 - + ((finesine[(y*128 + timebase*5 + 900) & FINEMASK]*2)>>FRACBITS) - + ((finesine[(x*256 + timebase*4 + 300) & FINEMASK]*2)>>FRACBITS)) & xmask; - int yt = (y + 128 - + ((finesine[(y*128 + timebase*3 + 700) & FINEMASK]*2)>>FRACBITS) - + ((finesine[(x*256 + timebase*4 + 1200) & FINEMASK]*2)>>FRACBITS)) & ymask; - const BYTE *source = otherpix + (xt << ybits) + yt; - BYTE *dest = Pixels + (x << ybits) + y; - *dest = *source; - } - } -} - -FCanvasTexture::FCanvasTexture (const char *name, int width, int height) -{ - strncpy (Name, name, 8); - Name[8] = 0; - Width = width; - Height = height; - LeftOffset = TopOffset = 0; - CalcBitSize (); - - bMasked = false; - DummySpans[0].TopOffset = 0; - DummySpans[0].Length = height; - DummySpans[1].TopOffset = 0; - DummySpans[1].Length = 0; - UseType = TEX_Wall; - Canvas = NULL; - bNeedsUpdate = true; - bDidUpdate = false; - bHasCanvas = true; - bFirstUpdate = true; -} - -FCanvasTexture::~FCanvasTexture () -{ - Unload (); -} - -const BYTE *FCanvasTexture::GetColumn (unsigned int column, const Span **spans_out) -{ - bNeedsUpdate = true; - if (Canvas == NULL) - { - MakeTexture (); - } - if ((unsigned)column >= (unsigned)Width) - { - if (WidthMask + 1 == Width) - { - column &= WidthMask; - } - else - { - column %= Width; - } - } - if (spans_out != NULL) - { - *spans_out = DummySpans; - } - return Pixels + column*Height; -} - -const BYTE *FCanvasTexture::GetPixels () -{ - bNeedsUpdate = true; - if (Canvas == NULL) - { - MakeTexture (); - } - return Pixels; -} - -void FCanvasTexture::MakeTexture () -{ - Canvas = new DSimpleCanvas (Width, Height); - Canvas->Lock (); - if (Width != Height || Width != Canvas->GetPitch()) - { - Pixels = new BYTE[Width*Height]; - } - else - { - Pixels = Canvas->GetBuffer(); - } - // Draw a special "unrendered" initial texture into the buffer. - memset (Pixels, 0, Width*Height/2); - memset (Pixels+Width*Height/2, 255, Width*Height/2); -} - -void FCanvasTexture::Unload () -{ - if (Canvas != NULL) - { - if (Pixels != NULL && Pixels != Canvas->GetBuffer()) - { - delete[] Pixels; - } - Pixels = NULL; - delete Canvas; - Canvas = NULL; - } -} - -bool FCanvasTexture::CheckModified () -{ - if (bDidUpdate) - { - bDidUpdate = false; - return true; - } - return false; -} - -void FCanvasTexture::RenderView (AActor *viewpoint, int fov) -{ - if (Canvas == NULL) - { - MakeTexture (); - } - float savedfov = LastFOV; - R_SetFOV (fov); - R_RenderViewToCanvas (viewpoint, Canvas, 0, 0, Width, Height, bFirstUpdate); - R_SetFOV (savedfov); - if (Pixels == Canvas->GetBuffer()) - { - FlipSquareBlock (Pixels, Width, Height); - } - else - { - FlipNonSquareBlock (Pixels, Canvas->GetBuffer(), Width, Height, Canvas->GetPitch()); - } - bNeedsUpdate = false; - bDidUpdate = true; - bFirstUpdate = false; -} // // R_InitTextures @@ -3131,98 +374,6 @@ void R_InitTextures (void) } } -// -// R_InitBuildTiles -// -// [RH] Support Build tiles! -// - -void R_InitBuildTiles () -{ - int numartfiles = 0; - char artfile[] = "tilesXXX.art"; - int lumpnum; - - lumpnum = Wads.CheckNumForFullName ("blood.pal"); - if (lumpnum >= 0) - { - // Blood's tiles are external resources. (Why did they do it like that?) - FString rffpath = Wads.GetWadFullName (Wads.GetLumpFile (lumpnum)); - int slashat = rffpath.LastIndexOf ('/'); - if (slashat >= 0) - { - rffpath.Truncate (slashat + 1); - } - else - { - rffpath += '/'; - } - - for (; numartfiles < 1000; numartfiles++) - { - artfile[5] = numartfiles / 100 + '0'; - artfile[6] = numartfiles / 10 % 10 + '0'; - artfile[7] = numartfiles % 10 + '0'; - - FString artpath = rffpath; - artpath += artfile; - - FILE *f = fopen (artpath, "rb"); - if (f == NULL) - { - break; - } - - size_t len = Q_filelength (f); - BYTE *art = new BYTE[len]; - if (fread (art, 1, len, f) != len || LittleLong(*(DWORD *)art) != 1) - { - delete[] art; - } - else - { - BuildTileFiles.Push (art); - TexMan.AddTiles (art); - } - fclose (f); - } - } - - for (; numartfiles < 1000; numartfiles++) - { - artfile[5] = numartfiles / 100 + '0'; - artfile[6] = numartfiles / 10 % 10 + '0'; - artfile[7] = numartfiles % 10 + '0'; - lumpnum = Wads.CheckNumForFullName (artfile); - if (lumpnum < 0) - { - break; - } - - BYTE *art = new BYTE[Wads.LumpLength (lumpnum)]; - Wads.ReadLump (lumpnum, art); - - if (LittleLong(*(DWORD *)art) != 1) - { - delete[] art; - } - else - { - BuildTileFiles.Push (art); - TexMan.AddTiles (art); - } - } -} - -void R_DeinitBuildTiles () -{ - for (unsigned int i = 0; i < BuildTileFiles.Size(); ++i) - { - delete[] BuildTileFiles[i]; - } - BuildTileFiles.Clear(); -} - static struct FakeCmap { char name[8]; PalEntry blend; @@ -3402,12 +553,14 @@ DWORD R_BlendForColormap (DWORD map) // void R_InitData () { - TexMan.AddSprites (); + FTexture::InitGrayMap(); + TexMan.AddGroup("S_START", "S_END", ns_sprites, FTexture::TEX_Sprite); R_InitPatches (); R_InitTextures (); - TexMan.AddFlats (); + TexMan.AddGroup("F_START", "F_END", ns_flats, FTexture::TEX_Flat); R_InitBuildTiles (); - TexMan.AddExtraTextures (); + TexMan.AddGroup("TX_START", "TX_END", ns_newtextures, FTexture::TEX_Override); + TexMan.DefaultTexture = TexMan.CheckForTexture ("-NOFLAT-", FTexture::TEX_Override, 0); R_InitColormaps (); C_InitConsole (SCREENWIDTH, SCREENHEIGHT, true); @@ -3673,9 +826,6 @@ static void R_InitPatches () "HELP0", "TITLEPIC", "ENDPIC", - "FINALE1", - "FINALE2", - "FINALE3", "STTPRCNT", "STARMS", "VICTORY2", diff --git a/src/r_data.h b/src/r_data.h index 975d70fe4..e18ea1fbf 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -46,7 +46,6 @@ public: class FPatchTexture : public FTexture { public: - FPatchTexture (int lumpnum, int usetype); ~FPatchTexture (); const BYTE *GetColumn (unsigned int column, const Span **spans_out); @@ -58,10 +57,14 @@ protected: BYTE *Pixels; Span **Spans; + static bool Check(FileReader & file); + static FTexture *Create(FileReader & file, int lumpnum); + FPatchTexture (int lumpnum, patch_t *header); + virtual void MakeTexture (); void HackHack (int newheight); - void GetDimensions (); + friend class FTexture; friend class FMultiPatchTexture; }; @@ -108,7 +111,6 @@ private: class FFlatTexture : public FTexture { public: - FFlatTexture (int lumpnum); ~FFlatTexture (); const BYTE *GetColumn (unsigned int column, const Span **spans_out); @@ -120,7 +122,13 @@ protected: BYTE *Pixels; Span DummySpans[2]; + static bool Check(FileReader & file); + static FTexture *Create(FileReader & file, int lumpnum); + FFlatTexture (int lumpnum); + void MakeTexture (); + + friend class FTexture; }; // A texture defined in a Build TILESxxx.ART file @@ -144,7 +152,6 @@ protected: class FRawPageTexture : public FTexture { public: - FRawPageTexture (int lumpnum); ~FRawPageTexture (); const BYTE *GetColumn (unsigned int column, const Span **spans_out); @@ -152,19 +159,50 @@ public: void Unload (); protected: + static bool Check(FileReader & file); + static FTexture *Create(FileReader & file, int lumpnum); + FRawPageTexture (int lumpnum); + int SourceLump; BYTE *Pixels; static const Span DummySpans[2]; void MakeTexture (); + + friend class FTexture; }; +// A raw 320x? graphic used by Heretic and Hexen for the automap parchment +class FAutomapTexture : public FTexture +{ +public: + ~FAutomapTexture (); + + const BYTE *GetColumn (unsigned int column, const Span **spans_out); + const BYTE *GetPixels (); + void Unload (); + void MakeTexture (); + +private: + + static bool Check(FileReader & file); + static FTexture *Create(FileReader & file, int lumpnum); + FAutomapTexture (int lumpnum); + + + BYTE *Pixels; + Span DummySpan[2]; + int LumpNum; + + friend class FTexture; +}; + + // An IMGZ image (mostly just crosshairs) class FIMGZTexture : public FTexture { public: - FIMGZTexture (int lumpnum); ~FIMGZTexture (); const BYTE *GetColumn (unsigned int column, const Span **spans_out); @@ -172,14 +210,19 @@ public: void Unload (); protected: + + static bool Check(FileReader & file); + static FTexture *Create(FileReader & file, int lumpnum); + FIMGZTexture (int lumpnum, WORD w, WORD h, SWORD l, SWORD t); + int SourceLump; BYTE *Pixels; Span **Spans; - void GetDimensions (); void MakeTexture (); struct ImageHeader; + friend class FTexture; }; @@ -187,7 +230,6 @@ protected: class FPNGTexture : public FTexture { public: - FPNGTexture (int lumpnum, int width, int height, BYTE bitdepth, BYTE colortype, BYTE interlace); ~FPNGTexture (); const BYTE *GetColumn (unsigned int column, const Span **spans_out); @@ -195,6 +237,12 @@ public: void Unload (); protected: + + static bool Check(FileReader & file); + static FTexture *Create(FileReader & file, int lumpnum); + FPNGTexture (FileReader &lump, int lumpnum, int width, int height, BYTE bitdepth, BYTE colortype, BYTE interlace); + + int SourceLump; BYTE *Pixels; Span **Spans; @@ -208,6 +256,8 @@ protected: DWORD StartOfIDAT; void MakeTexture (); + + friend class FTexture; }; @@ -216,19 +266,50 @@ protected: class FJPEGTexture : public FTexture { public: - FJPEGTexture (int lumpnum, int width, int height); ~FJPEGTexture (); const BYTE *GetColumn (unsigned int column, const Span **spans_out); const BYTE *GetPixels (); void Unload (); +protected: + + static bool Check(FileReader & file); + static FTexture *Create(FileReader & file, int lumpnum); + FJPEGTexture (int lumpnum, int width, int height); + + int SourceLump; + BYTE *Pixels; + Span DummySpans[2]; + + void MakeTexture (); + + friend class FTexture; +}; + +// A texture that is just a single patch +class FTGATexture : public FTexture +{ +public: + ~FTGATexture (); + + const BYTE *GetColumn (unsigned int column, const Span **spans_out); + const BYTE *GetPixels (); + void Unload (); + protected: int SourceLump; BYTE *Pixels; - Span DummySpans[2]; + Span **Spans; - void MakeTexture (); + static bool Check(FileReader & file); + static FTexture *Create(FileReader & file, int lumpnum); + FTGATexture (int lumpnum, int width, int height); + void ReadCompressed(FileReader &lump, BYTE * buffer, int bytesperpixel); + + virtual void MakeTexture (); + + friend class FTexture; }; // A texture that returns a wiggly version of another texture. diff --git a/src/r_defs.h b/src/r_defs.h index 72f8cd572..da3e55a8d 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -586,11 +586,13 @@ struct patch_t // the [0] is &columnofs[width] }; +class FileReader; + // Base texture class class FTexture { public: - FTexture (); + static FTexture *CreateTexture(int lumpnum, int usetype); virtual ~FTexture (); SWORD LeftOffset, TopOffset; @@ -608,6 +610,7 @@ public: BYTE bAlphaTexture:1; // Texture is an alpha channel without color information BYTE bHasCanvas:1; // Texture is based off FCanvasTexture BYTE bWarped:2; // This is a warped texture. Used to avoid multiple warps on one texture + BYTE bIsPatch:1; // 1 if an FPatchTexture. Required to fix FMultipatchTexture::CheckForHacks WORD Rotations; @@ -624,6 +627,7 @@ public: TEX_MiscPatch, TEX_FontChar, TEX_Override, // For patches between TX_START/TX_END + TEX_Autopage, // Automap background - used to enable the use of FAutomapTexture TEX_Null, }; @@ -641,8 +645,8 @@ public: virtual void Unload () = 0; - int GetWidth () { if (Width == 0xFFFF) { GetDimensions(); } return Width; } - int GetHeight () { if (Width == 0xFFFF) { GetDimensions(); } return Height; } + int GetWidth () { return Width; } + int GetHeight () { return Height; } virtual void SetFrontSkyLayer(); @@ -652,11 +656,13 @@ public: // last call to GetPixels(). This should be considered valid only if a call to CheckModified() // is immediately followed by a call to GetPixels(). virtual bool CheckModified (); + static void InitGrayMap(); protected: WORD Width, Height, WidthMask; + static BYTE GrayMap[256]; - virtual void GetDimensions (); + FTexture (); Span **CreateSpans (const BYTE *pixels) const; void FreeSpans (Span **spans) const; @@ -725,13 +731,10 @@ public: void AddTexturesLump (const void *lumpdata, int lumpsize, int patcheslump, int firstdup=0, bool texture1=false); void AddTexturesLumps (int lump1, int lump2, int patcheslump); - void AddFlats (); - void AddSprites (); + void AddGroup(const char * startlump, const char * endlump, int ns, int usetype); void AddPatches (int lumpnum); void AddTiles (void *tileFile); - void AddExtraTextures (); // Adds patches in the ns_newtextures namespace - static FTexture *DoCreateTexture (int lumpnum, int usetype=FTexture::TEX_Any); int CreateTexture (int lumpnum, int usetype=FTexture::TEX_Any); // Also calls AddTexture int AddTexture (FTexture *texture); int AddPatch (const char *patchname, int namespc=0); @@ -749,6 +752,7 @@ public: int NumTextures () const { return (int)Textures.Size(); } + private: struct TextureHash { @@ -760,6 +764,8 @@ private: TArray<WORD> Translation; WORD HashFirst[HASH_SIZE]; int DefaultTexture; + + friend void R_InitData (); }; extern FTextureManager TexMan; diff --git a/src/textures/automaptexture.cpp b/src/textures/automaptexture.cpp new file mode 100644 index 000000000..b68dcefe4 --- /dev/null +++ b/src/textures/automaptexture.cpp @@ -0,0 +1,122 @@ +/* +** automaptexture.cpp +** Texture class for Raven's automap parchment +** +**--------------------------------------------------------------------------- +** 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. +**--------------------------------------------------------------------------- +** +** This texture type is only used as a last resort when everything else has failed for creating +** the AUTOPAGE texture. That's because Raven used a raw lump of non-standard proportions to define it. +** +*/ + +#include "doomtype.h" +#include "files.h" +#include "r_data.h" +#include "w_wad.h" + +bool FAutomapTexture::Check(FileReader & data) +{ + if (data.GetLength() < 320) return false; + return true; +} + +FTexture *FAutomapTexture::Create(FileReader & file, int lumpnum) +{ + return new FAutomapTexture(lumpnum); +} + +FAutomapTexture::FAutomapTexture (int lumpnum) +: Pixels(NULL), LumpNum(lumpnum) +{ + Width = 320; + Height = Wads.LumpLength(lumpnum) / 320; + CalcBitSize (); + + DummySpan[0].TopOffset = 0; + DummySpan[0].Length = Height; + DummySpan[1].TopOffset = 0; + DummySpan[1].Length = 0; +} + +FAutomapTexture::~FAutomapTexture () +{ + Unload (); +} + +void FAutomapTexture::Unload () +{ + if (Pixels != NULL) + { + delete[] Pixels; + Pixels = NULL; + } +} + +void FAutomapTexture::MakeTexture () +{ + int x, y; + FMemLump data = Wads.ReadLump (LumpNum); + const BYTE *indata = (const BYTE *)data.GetMem(); + + Pixels = new BYTE[Width * Height]; + + for (x = 0; x < Width; ++x) + { + for (y = 0; y < Height; ++y) + { + Pixels[x*Height+y] = indata[x+320*y]; + } + } +} + +const BYTE *FAutomapTexture::GetPixels () +{ + if (Pixels == NULL) + { + MakeTexture (); + } + return Pixels; +} + +const BYTE *FAutomapTexture::GetColumn (unsigned int column, const Span **spans_out) +{ + if (Pixels == NULL) + { + MakeTexture (); + } + if ((unsigned)column >= (unsigned)Width) + { + column %= Width; + } + if (spans_out != NULL) + { + *spans_out = DummySpan; + } + return Pixels + column*Height; +} diff --git a/src/textures/buildtexture.cpp b/src/textures/buildtexture.cpp new file mode 100644 index 000000000..4fdb2f8f2 --- /dev/null +++ b/src/textures/buildtexture.cpp @@ -0,0 +1,288 @@ +/* +** buildtexture.cpp +** Handling Build textures +** +**--------------------------------------------------------------------------- +** 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 "r_local.h" +#include "w_wad.h" +#include "templates.h" +#include "cmdlib.h" + +static TArray<BYTE *> BuildTileFiles; + +FBuildTexture::FBuildTexture (int tilenum, const BYTE *pixels, int width, int height, int left, int top) +: Pixels (pixels), Spans (NULL) +{ + Width = width; + Height = height; + LeftOffset = left; + TopOffset = top; + CalcBitSize (); + sprintf (Name, "BTIL%04d", tilenum); + UseType = TEX_Build; +} + +FBuildTexture::~FBuildTexture () +{ + if (Spans != NULL) + { + FreeSpans (Spans); + Spans = NULL; + } +} + +void FBuildTexture::Unload () +{ + // Nothing to do, since the pixels are accessed from memory-mapped files directly +} + +const BYTE *FBuildTexture::GetPixels () +{ + return Pixels; +} + +const BYTE *FBuildTexture::GetColumn (unsigned int column, const Span **spans_out) +{ + if (column >= Width) + { + if (WidthMask + 1 == Width) + { + column &= WidthMask; + } + else + { + column %= Width; + } + } + if (spans_out != NULL) + { + if (Spans == NULL) + { + Spans = CreateSpans (Pixels); + } + *spans_out = Spans[column]; + } + return Pixels + column*Height; +} + + + +static void AddTiles (void *tiles) +{ +// int numtiles = LittleLong(((DWORD *)tiles)[1]); // This value is not reliable + int tilestart = LittleLong(((DWORD *)tiles)[2]); + int tileend = LittleLong(((DWORD *)tiles)[3]); + const WORD *tilesizx = &((const WORD *)tiles)[8]; + const WORD *tilesizy = &tilesizx[tileend - tilestart + 1]; + const DWORD *picanm = (const DWORD *)&tilesizy[tileend - tilestart + 1]; + BYTE *tiledata = (BYTE *)&picanm[tileend - tilestart + 1]; + + for (int i = tilestart; i <= tileend; ++i) + { + int pic = i - tilestart; + int width = LittleShort(tilesizx[pic]); + int height = LittleShort(tilesizy[pic]); + DWORD anm = LittleLong(picanm[pic]); + int xoffs = (SBYTE)((anm >> 8) & 255) + width/2; + int yoffs = (SBYTE)((anm >> 16) & 255) + height/2; + int size = width*height; + int texnum; + FTexture *tex; + + if (width <= 0 || height <= 0) continue; + + tex = new FBuildTexture (i, tiledata, width, height, xoffs, yoffs); + texnum = TexMan.AddTexture (tex); + while (size > 0) + { + *tiledata = 255 - *tiledata; + tiledata++; + size--; + } + + if ((picanm[pic] & 63) && (picanm[pic] & 192)) + { + int type, speed; + + switch (picanm[pic] & 192) + { + case 64: type = 2; break; + case 128: type = 0; break; + case 192: type = 1; break; + default: type = 0; break; // Won't happen, but GCC bugs me if I don't put this here. + } + + speed = (anm >> 24) & 15; + speed = MAX (1, (1 << speed) * 1000 / 120); // Convert from 120 Hz to 1000 Hz. + + R_AddSimpleAnim (texnum, picanm[pic] & 63, type, speed); + } + + // Blood's rotation types: + // 0 - Single + // 1 - 5 Full + // 2 - 8 Full + // 3 - Bounce (looks no different from Single; seems to signal bouncy sprites) + // 4 - 5 Half (not used in game) + // 5 - 3 Flat (not used in game) + // 6 - Voxel + // 7 - Spin Voxel + + int rotType = (anm >> 28) & 7; + if (rotType == 1) + { + spriteframe_t rot; + rot.Texture[0] = texnum; + rot.Texture[1] = texnum; + for (int j = 1; j < 4; ++j) + { + rot.Texture[j*2] = texnum + j; + rot.Texture[j*2+1] = texnum + j; + rot.Texture[16-j*2] = texnum + j; + rot.Texture[17-j*2] = texnum + j; + } + rot.Texture[8] = texnum + 4; + rot.Texture[9] = texnum + 4; + rot.Flip = 0x00FC; + tex->Rotations = SpriteFrames.Push (rot); + } + else if (rotType == 2) + { + spriteframe_t rot; + rot.Texture[0] = texnum; + rot.Texture[1] = texnum; + for (int j = 1; j < 8; ++j) + { + rot.Texture[16-j*2] = texnum + j; + rot.Texture[17-j*2] = texnum + j; + } + rot.Flip = 0; + tex->Rotations = SpriteFrames.Push (rot); + } + } +} + +// +// R_InitBuildTiles +// +// [RH] Support Build tiles! +// + +void R_InitBuildTiles () +{ + int numartfiles = 0; + char artfile[] = "tilesXXX.art"; + int lumpnum; + + lumpnum = Wads.CheckNumForFullName ("blood.pal"); + if (lumpnum >= 0) + { + // Blood's tiles are external resources. (Why did they do it like that?) + FString rffpath = Wads.GetWadFullName (Wads.GetLumpFile (lumpnum)); + int slashat = rffpath.LastIndexOf ('/'); + if (slashat >= 0) + { + rffpath.Truncate (slashat + 1); + } + else + { + rffpath += '/'; + } + + for (; numartfiles < 1000; numartfiles++) + { + artfile[5] = numartfiles / 100 + '0'; + artfile[6] = numartfiles / 10 % 10 + '0'; + artfile[7] = numartfiles % 10 + '0'; + + FString artpath = rffpath; + artpath += artfile; + + FILE *f = fopen (artpath, "rb"); + if (f == NULL) + { + break; + } + + size_t len = Q_filelength (f); + BYTE *art = new BYTE[len]; + if (fread (art, 1, len, f) != len || LittleLong(*(DWORD *)art) != 1) + { + delete[] art; + } + else + { + BuildTileFiles.Push (art); + AddTiles (art); + } + fclose (f); + } + } + + for (; numartfiles < 1000; numartfiles++) + { + artfile[5] = numartfiles / 100 + '0'; + artfile[6] = numartfiles / 10 % 10 + '0'; + artfile[7] = numartfiles % 10 + '0'; + lumpnum = Wads.CheckNumForFullName (artfile); + if (lumpnum < 0) + { + break; + } + + BYTE *art = new BYTE[Wads.LumpLength (lumpnum)]; + Wads.ReadLump (lumpnum, art); + + if (LittleLong(*(DWORD *)art) != 1) + { + delete[] art; + } + else + { + BuildTileFiles.Push (art); + AddTiles (art); + } + } +} + +void R_DeinitBuildTiles () +{ + for (unsigned int i = 0; i < BuildTileFiles.Size(); ++i) + { + delete[] BuildTileFiles[i]; + } + BuildTileFiles.Clear(); +} + diff --git a/src/textures/canvastexture.cpp b/src/textures/canvastexture.cpp new file mode 100644 index 000000000..25fbeb1f3 --- /dev/null +++ b/src/textures/canvastexture.cpp @@ -0,0 +1,167 @@ +/* +** canvastexture.cpp +** Texture class for camera textures +** +**--------------------------------------------------------------------------- +** 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 "r_local.h" + +extern float LastFOV; + +FCanvasTexture::FCanvasTexture (const char *name, int width, int height) +{ + strncpy (Name, name, 8); + Name[8] = 0; + Width = width; + Height = height; + LeftOffset = TopOffset = 0; + CalcBitSize (); + + bMasked = false; + DummySpans[0].TopOffset = 0; + DummySpans[0].Length = height; + DummySpans[1].TopOffset = 0; + DummySpans[1].Length = 0; + UseType = TEX_Wall; + Canvas = NULL; + bNeedsUpdate = true; + bDidUpdate = false; + bHasCanvas = true; + bFirstUpdate = true; +} + +FCanvasTexture::~FCanvasTexture () +{ + Unload (); +} + +const BYTE *FCanvasTexture::GetColumn (unsigned int column, const Span **spans_out) +{ + bNeedsUpdate = true; + if (Canvas == NULL) + { + MakeTexture (); + } + if ((unsigned)column >= (unsigned)Width) + { + if (WidthMask + 1 == Width) + { + column &= WidthMask; + } + else + { + column %= Width; + } + } + if (spans_out != NULL) + { + *spans_out = DummySpans; + } + return Pixels + column*Height; +} + +const BYTE *FCanvasTexture::GetPixels () +{ + bNeedsUpdate = true; + if (Canvas == NULL) + { + MakeTexture (); + } + return Pixels; +} + +void FCanvasTexture::MakeTexture () +{ + Canvas = new DSimpleCanvas (Width, Height); + Canvas->Lock (); + if (Width != Height || Width != Canvas->GetPitch()) + { + Pixels = new BYTE[Width*Height]; + } + else + { + Pixels = Canvas->GetBuffer(); + } + // Draw a special "unrendered" initial texture into the buffer. + memset (Pixels, 0, Width*Height/2); + memset (Pixels+Width*Height/2, 255, Width*Height/2); +} + +void FCanvasTexture::Unload () +{ + if (Canvas != NULL) + { + if (Pixels != NULL && Pixels != Canvas->GetBuffer()) + { + delete[] Pixels; + } + Pixels = NULL; + delete Canvas; + Canvas = NULL; + } +} + +bool FCanvasTexture::CheckModified () +{ + if (bDidUpdate) + { + bDidUpdate = false; + return true; + } + return false; +} + +void FCanvasTexture::RenderView (AActor *viewpoint, int fov) +{ + if (Canvas == NULL) + { + MakeTexture (); + } + float savedfov = LastFOV; + R_SetFOV ((float)fov); + R_RenderViewToCanvas (viewpoint, Canvas, 0, 0, Width, Height, bFirstUpdate); + R_SetFOV (savedfov); + if (Pixels == Canvas->GetBuffer()) + { + FlipSquareBlock (Pixels, Width, Height); + } + else + { + FlipNonSquareBlock (Pixels, Canvas->GetBuffer(), Width, Height, Canvas->GetPitch()); + } + bNeedsUpdate = false; + bDidUpdate = true; + bFirstUpdate = false; +} + diff --git a/src/textures/flattexture.cpp b/src/textures/flattexture.cpp new file mode 100644 index 000000000..271c47119 --- /dev/null +++ b/src/textures/flattexture.cpp @@ -0,0 +1,152 @@ +/* +** flattexture.cpp +** Texture class for standard Doom flats +** +**--------------------------------------------------------------------------- +** 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 "r_data.h" +#include "w_wad.h" + + +bool FFlatTexture::Check(FileReader & file) +{ + return true; +} + +FTexture *FFlatTexture::Create(FileReader & file, int lumpnum) +{ + return new FFlatTexture(lumpnum); +} + +FFlatTexture::FFlatTexture (int lumpnum) +: SourceLump(lumpnum), Pixels(0) +{ + int area; + int bits; + + Wads.GetLumpName (Name, lumpnum); + Name[8] = 0; + area = Wads.LumpLength (lumpnum); + + switch (area) + { + default: + case 64*64: bits = 6; break; + case 8*8: bits = 3; break; + case 16*16: bits = 4; break; + case 32*32: bits = 5; break; + case 128*128: bits = 7; break; + case 256*256: bits = 8; break; + } + + bMasked = false; + WidthBits = HeightBits = bits; + Width = Height = 1 << bits; + WidthMask = (1 << bits) - 1; + DummySpans[0].TopOffset = 0; + DummySpans[0].Length = Height; + DummySpans[1].TopOffset = 0; + DummySpans[1].Length = 0; + + /* + if (bits > 6) + { + ScaleX = ScaleY = 8 << (bits - 6); + } + else + { + ScaleX = ScaleY = 8; + } + */ +} + +FFlatTexture::~FFlatTexture () +{ + Unload (); +} + +void FFlatTexture::Unload () +{ + if (Pixels != NULL) + { + delete[] Pixels; + Pixels = NULL; + } +} + +const BYTE *FFlatTexture::GetColumn (unsigned int column, const Span **spans_out) +{ + if (Pixels == NULL) + { + MakeTexture (); + } + if ((unsigned)column >= (unsigned)Width) + { + if (WidthMask + 1 == Width) + { + column &= WidthMask; + } + else + { + column %= Width; + } + } + if (spans_out != NULL) + { + *spans_out = DummySpans; + } + return Pixels + column*Height; +} + +const BYTE *FFlatTexture::GetPixels () +{ + if (Pixels == NULL) + { + MakeTexture (); + } + return Pixels; +} + +void FFlatTexture::MakeTexture () +{ + FWadLump lump = Wads.OpenLumpNum (SourceLump); + Pixels = new BYTE[Width*Height]; + long numread = lump.Read (Pixels, Width*Height); + if (numread < Width*Height) + { + memset (Pixels + numread, 0xBB, Width*Height - numread); + } + FlipSquareBlockRemap (Pixels, Width, Height, GPalette.Remap); +} + diff --git a/src/textures/imgztexture.cpp b/src/textures/imgztexture.cpp new file mode 100644 index 000000000..1e888b562 --- /dev/null +++ b/src/textures/imgztexture.cpp @@ -0,0 +1,224 @@ +/* +** imgztexture.cpp +** Texture class for IMGZ style images +** +**--------------------------------------------------------------------------- +** 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 "r_data.h" +#include "w_wad.h" + +// [RH] Just a format I invented to avoid WinTex's palette remapping +// when I wanted to insert some alpha maps. + +struct FIMGZTexture::ImageHeader +{ + BYTE Magic[4]; + WORD Width; + WORD Height; + SWORD LeftOffset; + SWORD TopOffset; + BYTE Compression; + BYTE Reserved[11]; +}; + + +bool FIMGZTexture::Check(FileReader & file) +{ + DWORD id; + file.Seek(0, SEEK_SET); + file.Read(&id, 4); + return (id == MAKE_ID('I','M','G','Z')); +} + +FTexture *FIMGZTexture::Create(FileReader & file, int lumpnum) +{ + DWORD magic; + WORD w, h; + SWORD l, t; + + file.Seek(0, SEEK_SET); + file >> magic >> w >> h >> l >> t; + return new FIMGZTexture(lumpnum, w, h, l, t); +} + +FIMGZTexture::FIMGZTexture (int lumpnum, WORD w, WORD h, SWORD l, SWORD t) + : SourceLump(lumpnum), Pixels(0), Spans(0) +{ + Wads.GetLumpName (Name, lumpnum); + Name[8] = 0; + Width = w; + Height = h; + LeftOffset = l; + TopOffset = t; + CalcBitSize (); +} + +FIMGZTexture::~FIMGZTexture () +{ + Unload (); + if (Spans != NULL) + { + FreeSpans (Spans); + Spans = NULL; + } +} + +void FIMGZTexture::Unload () +{ + if (Pixels != NULL) + { + delete[] Pixels; + Pixels = NULL; + } +} + +const BYTE *FIMGZTexture::GetColumn (unsigned int column, const Span **spans_out) +{ + if (Pixels == NULL) + { + MakeTexture (); + } + if ((unsigned)column >= (unsigned)Width) + { + if (WidthMask + 1 == Width) + { + column &= WidthMask; + } + else + { + column %= Width; + } + } + if (spans_out != NULL) + { + *spans_out = Spans[column]; + } + return Pixels + column*Height; +} + +const BYTE *FIMGZTexture::GetPixels () +{ + if (Pixels == NULL) + { + MakeTexture (); + } + return Pixels; +} + +void FIMGZTexture::MakeTexture () +{ + FMemLump lump = Wads.ReadLump (SourceLump); + const ImageHeader *imgz = (const ImageHeader *)lump.GetMem(); + const BYTE *data = (const BYTE *)&imgz[1]; + + if (Width != 0xFFFF) + { + Width = LittleShort(imgz->Width); + Height = LittleShort(imgz->Height); + LeftOffset = LittleShort(imgz->LeftOffset); + TopOffset = LittleShort(imgz->TopOffset); + } + + BYTE *dest_p; + int dest_adv = Height; + int dest_rew = Width * Height - 1; + + CalcBitSize (); + Pixels = new BYTE[Width*Height]; + dest_p = Pixels; + + // Convert the source image from row-major to column-major format + if (!imgz->Compression) + { + for (int y = Height; y != 0; --y) + { + for (int x = Width; x != 0; --x) + { + *dest_p = *data; + dest_p += dest_adv; + data++; + } + dest_p -= dest_rew; + } + } + else + { + // IMGZ compression is the same RLE used by IFF ILBM files + int runlen = 0, setlen = 0; + BYTE setval = 0; // Shut up, GCC + + for (int y = Height; y != 0; --y) + { + for (int x = Width; x != 0; ) + { + if (runlen != 0) + { + BYTE color = *data; + *dest_p = color; + dest_p += dest_adv; + data++; + x--; + runlen--; + } + else if (setlen != 0) + { + *dest_p = setval; + dest_p += dest_adv; + x--; + setlen--; + } + else + { + SBYTE code = *data++; + if (code >= 0) + { + runlen = code + 1; + } + else if (code != -128) + { + setlen = (-code) + 1; + setval = *data++; + } + } + } + dest_p -= dest_rew; + } + } + + if (Spans == NULL) + { + Spans = CreateSpans (Pixels); + } +} + diff --git a/src/textures/jpegtexture.cpp b/src/textures/jpegtexture.cpp new file mode 100644 index 000000000..946b9cbe1 --- /dev/null +++ b/src/textures/jpegtexture.cpp @@ -0,0 +1,316 @@ +/* +** jpegtexture.cpp +** Texture class for JPEG images +** +**--------------------------------------------------------------------------- +** Copyright 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 "r_data.h" +#include "r_jpeg.h" +#include "w_wad.h" +#include "v_text.h" + +void FLumpSourceMgr::InitSource (j_decompress_ptr cinfo) +{ + ((FLumpSourceMgr *)(cinfo->src))->StartOfFile = true; +} + +boolean FLumpSourceMgr::FillInputBuffer (j_decompress_ptr cinfo) +{ + FLumpSourceMgr *me = (FLumpSourceMgr *)(cinfo->src); + long nbytes = me->Lump->Read (me->Buffer, sizeof(me->Buffer)); + + if (nbytes <= 0) + { + me->Buffer[0] = (JOCTET)0xFF; + me->Buffer[1] = (JOCTET)JPEG_EOI; + nbytes = 2; + } + me->next_input_byte = me->Buffer; + me->bytes_in_buffer = nbytes; + me->StartOfFile = false; + return TRUE; +} + +void FLumpSourceMgr::SkipInputData (j_decompress_ptr cinfo, long num_bytes) +{ + FLumpSourceMgr *me = (FLumpSourceMgr *)(cinfo->src); + if (num_bytes <= (long)me->bytes_in_buffer) + { + me->bytes_in_buffer -= num_bytes; + me->next_input_byte += num_bytes; + } + else + { + num_bytes -= (long)me->bytes_in_buffer; + me->Lump->Seek (num_bytes, SEEK_CUR); + FillInputBuffer (cinfo); + } +} + +void FLumpSourceMgr::TermSource (j_decompress_ptr cinfo) +{ +} + +void JPEG_ErrorExit (j_common_ptr cinfo) +{ + (*cinfo->err->output_message) (cinfo); + throw -1; +} + +void JPEG_OutputMessage (j_common_ptr cinfo) +{ + char buffer[JMSG_LENGTH_MAX]; + + (*cinfo->err->format_message) (cinfo, buffer); + Printf (TEXTCOLOR_ORANGE "JPEG failure: %s\n", buffer); +} + +bool FJPEGTexture::Check(FileReader & file) +{ + BYTE hdr[3]; + file.Seek(0, SEEK_SET); + file.Read(hdr, 3); + return (hdr[0] == 0xFF && hdr[1] == 0xD8 && hdr[2] == 0xFF); +} + +FTexture *FJPEGTexture::Create(FileReader & data, int lumpnum) +{ + union + { + DWORD dw; + WORD w[2]; + BYTE b[4]; + } first4bytes; + + data.Seek(0, SEEK_SET); + data.Read(&first4bytes, 4); + + // Find the SOFn marker to extract the image dimensions, + // where n is 0, 1, or 2 (other types are unsupported). + while ((unsigned)first4bytes.b[3] - 0xC0 >= 3) + { + if (data.Read (first4bytes.w, 2) != 2) + { + return NULL; + } + data.Seek (BigShort(first4bytes.w[0]) - 2, SEEK_CUR); + if (data.Read (first4bytes.b + 2, 2) != 2 || first4bytes.b[2] != 0xFF) + { + return NULL; + } + } + if (data.Read (first4bytes.b, 3) != 3) + { + return NULL; + } + if (BigShort (first4bytes.w[0]) <5) + { + return NULL; + } + if (data.Read (first4bytes.b, 4) != 4) + { + return NULL; + } + return new FJPEGTexture (lumpnum, BigShort(first4bytes.w[1]), BigShort(first4bytes.w[0])); +} + +FLumpSourceMgr::FLumpSourceMgr (FileReader *lump, j_decompress_ptr cinfo) +: Lump (lump) +{ + cinfo->src = this; + init_source = InitSource; + fill_input_buffer = FillInputBuffer; + skip_input_data = SkipInputData; + resync_to_restart = jpeg_resync_to_restart; + term_source = TermSource; + bytes_in_buffer = 0; + next_input_byte = NULL; +} + +FJPEGTexture::FJPEGTexture (int lumpnum, int width, int height) +: SourceLump(lumpnum), Pixels(0) +{ + Wads.GetLumpName (Name, lumpnum); + Name[8] = 0; + + UseType = TEX_MiscPatch; + LeftOffset = 0; + TopOffset = 0; + bMasked = false; + + Width = width; + Height = height; + CalcBitSize (); + + DummySpans[0].TopOffset = 0; + DummySpans[0].Length = Height; + DummySpans[1].TopOffset = 0; + DummySpans[1].Length = 0; +} + +FJPEGTexture::~FJPEGTexture () +{ + Unload (); +} + +void FJPEGTexture::Unload () +{ + if (Pixels != NULL) + { + delete[] Pixels; + Pixels = NULL; + } +} + +const BYTE *FJPEGTexture::GetColumn (unsigned int column, const Span **spans_out) +{ + if (Pixels == NULL) + { + MakeTexture (); + } + if ((unsigned)column >= (unsigned)Width) + { + if (WidthMask + 1 == Width) + { + column &= WidthMask; + } + else + { + column %= Width; + } + } + if (spans_out != NULL) + { + *spans_out = DummySpans; + } + return Pixels + column*Height; +} + +const BYTE *FJPEGTexture::GetPixels () +{ + if (Pixels == NULL) + { + MakeTexture (); + } + return Pixels; +} + +void FJPEGTexture::MakeTexture () +{ + FWadLump lump = Wads.OpenLumpNum (SourceLump); + JSAMPLE *buff = NULL; + + jpeg_decompress_struct cinfo; + jpeg_error_mgr jerr; + + Pixels = new BYTE[Width * Height]; + memset (Pixels, 0xBA, Width * Height); + + cinfo.err = jpeg_std_error(&jerr); + cinfo.err->output_message = JPEG_OutputMessage; + cinfo.err->error_exit = JPEG_ErrorExit; + jpeg_create_decompress(&cinfo); + try + { + FLumpSourceMgr sourcemgr(&lump, &cinfo); + jpeg_read_header(&cinfo, TRUE); + if (!((cinfo.out_color_space == JCS_RGB && cinfo.num_components == 3) || + (cinfo.out_color_space == JCS_CMYK && cinfo.num_components == 4) || + (cinfo.out_color_space == JCS_GRAYSCALE && cinfo.num_components == 1))) + { + Printf (TEXTCOLOR_ORANGE "Unsupported color format\n", Name); + throw -1; + } + + jpeg_start_decompress(&cinfo); + + int y = 0; + buff = new BYTE[cinfo.output_width * cinfo.output_components]; + + while (cinfo.output_scanline < cinfo.output_height) + { + int num_scanlines = jpeg_read_scanlines(&cinfo, &buff, 1); + BYTE *in = buff; + BYTE *out = Pixels + y; + switch (cinfo.out_color_space) + { + case JCS_RGB: + for (int x = Width; x > 0; --x) + { + *out = RGB32k[in[0]>>3][in[1]>>3][in[2]>>3]; + out += Height; + in += 3; + } + break; + + case JCS_GRAYSCALE: + for (int x = Width; x > 0; --x) + { + *out = GrayMap[in[0]]; + out += Height; + in += 1; + } + break; + + case JCS_CMYK: + // What are you doing using a CMYK image? :) + for (int x = Width; x > 0; --x) + { + // To be precise, these calculations should use 255, but + // 256 is much faster and virtually indistinguishable. + int r = in[3] - (((256-in[0])*in[3]) >> 8); + int g = in[3] - (((256-in[1])*in[3]) >> 8); + int b = in[3] - (((256-in[2])*in[3]) >> 8); + *out = RGB32k[r >> 3][g >> 3][b >> 3]; + out += Height; + in += 4; + } + break; + } + y++; + } + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + } + catch (int) + { + Printf (TEXTCOLOR_ORANGE " in texture %s\n", Name); + jpeg_destroy_decompress(&cinfo); + } + if (buff != NULL) + { + delete[] buff; + } +} + diff --git a/src/textures/multipatchtexture.cpp b/src/textures/multipatchtexture.cpp new file mode 100644 index 000000000..608dc0fff --- /dev/null +++ b/src/textures/multipatchtexture.cpp @@ -0,0 +1,534 @@ +/* +** multipatchtexture.cpp +** Texture class for standard Doom multipatch textures +** +**--------------------------------------------------------------------------- +** 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 "r_data.h" +#include "w_wad.h" +#include "i_system.h" +#include "gi.h" + +// On the Alpha, accessing the shorts directly if they aren't aligned on a +// 4-byte boundary causes unaligned access warnings. Why it does this at +// all and only while initing the textures is beyond me. + +#ifdef ALPHA +#define SAFESHORT(s) ((short)(((byte *)&(s))[0] + ((byte *)&(s))[1] * 256)) +#else +#define SAFESHORT(s) LittleShort(s) +#endif + + + +FMultiPatchTexture::FMultiPatchTexture (const void *texdef, FPatchLookup *patchlookup, int maxpatchnum, bool strife) +: Pixels (0), Spans(0), Parts(0), bRedirect(false) +{ + union + { + const maptexture_t *d; + const strifemaptexture_t *s; + } + mtexture; + + union + { + const mappatch_t *d; + const strifemappatch_t *s; + } + mpatch; + + int i; + + mtexture.d = (const maptexture_t *)texdef; + + if (strife) + { + NumParts = SAFESHORT(mtexture.s->patchcount); + } + else + { + NumParts = SAFESHORT(mtexture.d->patchcount); + } + + if (NumParts <= 0) + { + I_FatalError ("Bad texture directory"); + } + + UseType = FTexture::TEX_Wall; + Parts = new TexPart[NumParts]; + Width = SAFESHORT(mtexture.d->width); + Height = SAFESHORT(mtexture.d->height); + strncpy (Name, mtexture.d->name, 8); + Name[8] = 0; + + CalcBitSize (); + + // [RH] Special for beta 29: Values of 0 will use the tx/ty cvars + // to determine scaling instead of defaulting to 8. I will likely + // remove this once I finish the betas, because by then, users + // should be able to actually create scaled textures. + // 10-June-2003: It's still here long after beta 29. Heh. + + ScaleX = mtexture.d->ScaleX ? mtexture.d->ScaleX : 0; + ScaleY = mtexture.d->ScaleY ? mtexture.d->ScaleY : 0; + + if (mtexture.d->Flags & MAPTEXF_WORLDPANNING) + { + bWorldPanning = true; + } + + if (strife) + { + mpatch.s = &mtexture.s->patches[0]; + } + else + { + mpatch.d = &mtexture.d->patches[0]; + } + + for (i = 0; i < NumParts; ++i) + { + if (unsigned(LittleShort(mpatch.d->patch)) >= unsigned(maxpatchnum)) + { + I_FatalError ("Bad PNAMES and/or texture directory:\n\nPNAMES has %d entries, but\n%s wants to use entry %d.", + maxpatchnum, Name, LittleShort(mpatch.d->patch)+1); + } + Parts[i].OriginX = LittleShort(mpatch.d->originx); + Parts[i].OriginY = LittleShort(mpatch.d->originy); + Parts[i].Texture = patchlookup[LittleShort(mpatch.d->patch)].Texture; + if (Parts[i].Texture == NULL) + { + Printf ("Unknown patch %s in texture %s\n", patchlookup[LittleShort(mpatch.d->patch)].Name, Name); + NumParts--; + i--; + } + if (strife) + mpatch.s++; + else + mpatch.d++; + } + if (NumParts == 0) + { + Printf ("Texture %s is left without any patches\n", Name); + } + + CheckForHacks (); + + // If this texture is just a wrapper around a single patch, we can simply + // forward GetPixels() and GetColumn() calls to that patch. + if (NumParts == 1) + { + if (Parts->OriginX == 0 && Parts->OriginY == 0 && + Parts->Texture->GetWidth() == Width && + Parts->Texture->GetHeight() == Height) + { + bRedirect = true; + } + } +} + +FMultiPatchTexture::~FMultiPatchTexture () +{ + Unload (); + if (Parts != NULL) + { + delete[] Parts; + Parts = NULL; + } + if (Spans != NULL) + { + FreeSpans (Spans); + Spans = NULL; + } +} + +void FMultiPatchTexture::SetFrontSkyLayer () +{ + for (int i = 0; i < NumParts; ++i) + { + Parts[i].Texture->SetFrontSkyLayer (); + } + bNoRemap0 = true; +} + +void FMultiPatchTexture::Unload () +{ + if (Pixels != NULL) + { + delete[] Pixels; + Pixels = NULL; + } +} + +const BYTE *FMultiPatchTexture::GetPixels () +{ + if (bRedirect) + { + return Parts->Texture->GetPixels (); + } + if (Pixels == NULL) + { + MakeTexture (); + } + return Pixels; +} + +const BYTE *FMultiPatchTexture::GetColumn (unsigned int column, const Span **spans_out) +{ + if (bRedirect) + { + return Parts->Texture->GetColumn (column, spans_out); + } + if (Pixels == NULL) + { + MakeTexture (); + } + if ((unsigned)column >= (unsigned)Width) + { + if (WidthMask + 1 == Width) + { + column &= WidthMask; + } + else + { + column %= Width; + } + } + if (spans_out != NULL) + { + *spans_out = Spans[column]; + } + return Pixels + column*Height; +} + +void FMultiPatchTexture::MakeTexture () +{ + // Add a little extra space at the end if the texture's height is not + // a power of 2, in case somebody accidentally makes it repeat vertically. + int numpix = Width * Height + (1 << HeightBits) - Height; + + Pixels = new BYTE[numpix]; + memset (Pixels, 0, numpix); + + for (int i = 0; i < NumParts; ++i) + { + Parts[i].Texture->CopyToBlock (Pixels, Width, Height, + Parts[i].OriginX, Parts[i].OriginY); + } + + if (Spans == NULL) + { + Spans = CreateSpans (Pixels); + } +} + +void FMultiPatchTexture::CheckForHacks () +{ + if (NumParts <= 0) + { + return; + } + + // Heretic sky textures are marked as only 128 pixels tall, + // even though they are really 200 pixels tall. + if (gameinfo.gametype == GAME_Heretic && + Name[0] == 'S' && + Name[1] == 'K' && + Name[2] == 'Y' && + Name[4] == 0 && + Name[3] >= '1' && + Name[3] <= '3' && + Height == 128) + { + Height = 200; + HeightBits = 8; + return; + } + + // The Doom E1 sky has its patch's y offset at -8 instead of 0. + if (gameinfo.gametype == GAME_Doom && + !(gameinfo.flags & GI_MAPxx) && + NumParts == 1 && + Height == 128 && + Parts->OriginY == -8 && + Name[0] == 'S' && + Name[1] == 'K' && + Name[2] == 'Y' && + Name[3] == '1' && + Name[4] == 0) + { + Parts->OriginY = 0; + return; + } + + // BIGDOOR7 in Doom also has patches at y offset -4 instead of 0. + if (gameinfo.gametype == GAME_Doom && + !(gameinfo.flags & GI_MAPxx) && + NumParts == 2 && + Height == 128 && + Parts[0].OriginY == -4 && + Parts[1].OriginY == -4 && + Name[0] == 'B' && + Name[1] == 'I' && + Name[2] == 'G' && + Name[3] == 'D' && + Name[4] == 'O' && + Name[5] == 'O' && + Name[6] == 'R' && + Name[7] == '7') + { + Parts[0].OriginY = 0; + Parts[1].OriginY = 0; + return; + } + + // [RH] Some wads (I forget which!) have single-patch textures 256 + // pixels tall that have patch lengths recorded as 0. I can't think of + // any good reason for them to do this, and since I didn't make note + // of which wad made me hack in support for them, the hack is gone + // because I've added support for DeePsea's true tall patches. + // + // Okay, I found a wad with crap patches: Pleiades.wad's sky patches almost + // fit this description and are a big mess, but they're not single patch! + if (Height == 256) + { + int i; + + // All patches must be at the top of the texture for this fix + for (i = 0; i < NumParts; ++i) + { + if (Parts[i].OriginX != 0) + { + break; + } + } + + if (i == NumParts) + { + // This really must check whether the texture in question is + // actually an FPatchTexture before casting the pointer. + for (i = 0; i < NumParts; ++i) if (Parts[i].Texture->bIsPatch) + { + FPatchTexture *tex = (FPatchTexture *)Parts[i].Texture; + // 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 (tex->SourceLump); + const patch_t *realpatch = (patch_t *)lump.GetMem(); + const DWORD *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*)((byte*)realpatch+LittleLong(cofs[x])); + if (col->topdelta != 0 || col->length != 0) + { + break; // It's not bad! + } + col = (column_t *)((byte *)col + 256 + 4); + if (col->topdelta != 0xFF) + { + break; // More than one post in a column! + } + } + if (x == x2) + { // If all the columns were checked, it needs fixing. + tex->HackHack (Height); + } + } + } + } + } +} + +void FTextureManager::AddTexturesLump (const void *lumpdata, int lumpsize, int patcheslump, int firstdup, bool texture1) +{ + FPatchLookup *patchlookup; + int i, j; + DWORD numpatches; + + if (firstdup == 0) + { + firstdup = (int)Textures.Size(); + } + + { + FWadLump pnames = Wads.OpenLumpNum (patcheslump); + + pnames >> numpatches; + + // Check whether the amount of names reported is correct. + if (numpatches < 0) + { + Printf("Corrupt PNAMES lump found (negative amount of entries reported)"); + return; + } + + // Check whether the amount of names reported is correct. + int lumplength = Wads.LumpLength(patcheslump); + if (numpatches > DWORD((lumplength-4)/8)) + { + Printf("PNAMES lump is shorter than required (%ld entries reported but only %d bytes (%d entries) long\n", + numpatches, lumplength, (lumplength-4)/8); + // Truncate but continue reading. Who knows how many such lumps exist? + numpatches = (lumplength-4)/8; + } + + // Catalog the patches these textures use so we know which + // textures they represent. + patchlookup = (FPatchLookup *)alloca (numpatches * sizeof(*patchlookup)); + + for (DWORD i = 0; i < numpatches; ++i) + { + pnames.Read (patchlookup[i].Name, 8); + patchlookup[i].Name[8] = 0; + + j = CheckForTexture (patchlookup[i].Name, FTexture::TEX_WallPatch); + if (j >= 0) + { + patchlookup[i].Texture = Textures[j].Texture; + } + else + { + // Shareware Doom has the same PNAMES lump as the registered + // Doom, so printing warnings for patches that don't really + // exist isn't such a good idea. + //Printf ("Patch %s not found.\n", patchlookup[i].Name); + patchlookup[i].Texture = NULL; + } + } + } + + bool isStrife = false; + const DWORD *maptex, *directory; + DWORD maxoff; + int numtextures; + DWORD offset = 0; // Shut up, GCC! + + maptex = (const DWORD *)lumpdata; + numtextures = LittleLong(*maptex); + maxoff = lumpsize; + + if (maxoff < DWORD(numtextures+1)*4) + { + Printf ("Texture directory is too short"); + return; + } + + // Scan the texture lump to decide if it contains Doom or Strife textures + for (i = 0, directory = maptex+1; i < numtextures; ++i) + { + offset = LittleLong(directory[i]); + if (offset > maxoff) + { + Printf ("Bad texture directory"); + return; + } + + maptexture_t *tex = (maptexture_t *)((BYTE *)maptex + offset); + + // There is bizzarely a Doom editing tool that writes to the + // first two elements of columndirectory, so I can't check those. + if (SAFESHORT(tex->patchcount) <= 0 || + tex->columndirectory[2] != 0 || + tex->columndirectory[3] != 0) + { + isStrife = true; + break; + } + } + + + // Textures defined earlier in the lump take precedence over those defined later, + // but later TEXTUREx lumps take precedence over earlier ones. + for (i = 1, directory = maptex; i <= numtextures; ++i) + { + if (i == 1 && texture1) + { + // The very first texture is just a dummy. Copy its dimensions to texture 0. + // It still needs to be created in case someone uses it by name. + offset = LittleLong(directory[1]); + const maptexture_t *tex = (const maptexture_t *)((const BYTE *)maptex + offset); + FDummyTexture *tex0 = static_cast<FDummyTexture *>(Textures[0].Texture); + tex0->SetSize (SAFESHORT(tex->width), SAFESHORT(tex->height)); + } + + offset = LittleLong(directory[i]); + if (offset > maxoff) + { + Printf ("Bad texture directory"); + return; + } + + // If this texture was defined already in this lump, skip it + // This could cause problems with animations that use the same name for intermediate + // textures. Should I be worried? + for (j = (int)Textures.Size() - 1; j >= firstdup; --j) + { + if (strnicmp (Textures[j].Texture->Name, (const char *)maptex + offset, 8) == 0) + break; + } + if (j + 1 == firstdup) + { + FTexture *tex = new FMultiPatchTexture ((const BYTE *)maptex + offset, patchlookup, numpatches, isStrife); + if (i == 1 && texture1) + { + tex->UseType = FTexture::TEX_Null; + } + TexMan.AddTexture (tex); + } + } +} + + +void FTextureManager::AddTexturesLumps (int lump1, int lump2, int patcheslump) +{ + int firstdup = (int)Textures.Size(); + + if (lump1 >= 0) + { + FMemLump texdir = Wads.ReadLump (lump1); + AddTexturesLump (texdir.GetMem(), Wads.LumpLength (lump1), patcheslump, firstdup, true); + } + if (lump2 >= 0) + { + FMemLump texdir = Wads.ReadLump (lump2); + AddTexturesLump (texdir.GetMem(), Wads.LumpLength (lump2), patcheslump, firstdup, false); + } +} + diff --git a/src/textures/patchtexture.cpp b/src/textures/patchtexture.cpp new file mode 100644 index 000000000..d0289dd34 --- /dev/null +++ b/src/textures/patchtexture.cpp @@ -0,0 +1,400 @@ +/* +** 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 "r_data.h" +#include "w_wad.h" +#include "templates.h" + + +bool FPatchTexture::Check(FileReader & file) +{ + if (file.GetLength() < 13) return false; // minimum length of a valid Doom patch + + BYTE *data = new BYTE[file.GetLength()]; + file.Seek(0, SEEK_SET); + file.Read(data, file.GetLength()); + + const patch_t * foo = (const patch_t *)data; + + int height = LittleShort(foo->height); + int width = LittleShort(foo->width); + bool gapAtStart=true; + + 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) + { + DWORD ofs = LittleLong(foo->columnofs[x]); + if (ofs == (DWORD)width * 4 + 8) + { + gapAtStart = false; + } + else if (ofs >= (DWORD)(file.GetLength())) // Need one byte for an empty column (but there's patches that don't know that!) + { + delete [] data; + return false; + } + } + delete [] data; + return !gapAtStart; + } + delete [] data; + return false; +} + +FTexture *FPatchTexture::Create(FileReader & file, int lumpnum) +{ + patch_t header; + + file.Seek(0, SEEK_SET); + file >> header.width >> header.height >> header.leftoffset >> header.topoffset; + return new FPatchTexture(lumpnum, &header); +} + +FPatchTexture::FPatchTexture (int lumpnum, patch_t * header) +: SourceLump(lumpnum), Pixels(0), Spans(0) +{ + Wads.GetLumpName (Name, lumpnum); + Name[8] = 0; + bIsPatch = true; + Width = header->width; + Height = header->height; + LeftOffset = header->leftoffset; + TopOffset = header->topoffset; +} + +FPatchTexture::~FPatchTexture () +{ + Unload (); + if (Spans != NULL) + { + FreeSpans (Spans); + Spans = NULL; + } +} + +void FPatchTexture::Unload () +{ + if (Pixels != NULL) + { + delete[] Pixels; + Pixels = NULL; + } +} + +const BYTE *FPatchTexture::GetPixels () +{ + if (Pixels == NULL) + { + MakeTexture (); + } + return Pixels; +} + +const BYTE *FPatchTexture::GetColumn (unsigned int column, const Span **spans_out) +{ + if (Pixels == NULL) + { + MakeTexture (); + } + if ((unsigned)column >= (unsigned)Width) + { + if (WidthMask + 1 == Width) + { + column &= WidthMask; + } + else + { + column %= Width; + } + } + if (spans_out != NULL) + { + *spans_out = Spans[column]; + } + return Pixels + column*Height; +} + + +void FPatchTexture::MakeTexture () +{ + BYTE *remap, remaptable[256]; + Span *spanstuffer, *spanstarter; + const column_t *maxcol; + bool warned; + int numspans; + int x; + + FMemLump lump = Wads.ReadLump (SourceLump); + const patch_t *patch = (const patch_t *)lump.GetMem(); + + maxcol = (const column_t *)((const BYTE *)patch + Wads.LumpLength (SourceLump) - 3); + + // Check for badly-sized patches + if (LittleShort(patch->width) <= 0 || LittleShort(patch->height) <= 0) + { + lump = Wads.ReadLump ("-BADPATC"); + patch = (const patch_t *)lump.GetMem(); + Printf (PRINT_BOLD, "Patch %s has a non-positive size.\n", Name); + } + else if (LittleShort(patch->width) > 2048 || LittleShort(patch->height) > 2048) + { + lump = Wads.ReadLump ("-BADPATC"); + patch = (const patch_t *)lump.GetMem(); + Printf (PRINT_BOLD, "Patch %s is too big.\n", Name); + } + + if (Width == 0xFFFF) + { + Width = LittleShort(patch->width); + Height = LittleShort(patch->height); + LeftOffset = LittleShort(patch->leftoffset); + TopOffset = LittleShort(patch->topoffset); + } + CalcBitSize (); + + // Add a little extra space at the end if the texture's height is not + // a power of 2, in case somebody accidentally makes it repeat vertically. + int numpix = Width * Height + (1 << HeightBits) - Height; + + numspans = Width; + + Pixels = new BYTE[numpix]; + memset (Pixels, 0, numpix); + + if (bNoRemap0) + { + memcpy (remaptable, GPalette.Remap, 256); + remaptable[0] = 0; + remap = remaptable; + } + else + { + remap = GPalette.Remap; + } + + // Draw the image to the buffer + for (x = 0; x < Width; ++x) + { + BYTE *outtop = Pixels + x*Height; + const column_t *column = (const column_t *)((const BYTE *)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; + BYTE *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 BYTE *in = (const BYTE *)column + 3; + for (int i = 0; i < len; ++i) + { + out[i] = remap[in[i]]; + } + } + } + column = (const column_t *)((const BYTE *)column + column->length + 4); + } + } + + // Create the spans + if (Spans != NULL) + { + return; + } + + Spans = (Span **)M_Malloc (sizeof(Span*)*Width + sizeof(Span)*numspans); + spanstuffer = (Span *)((BYTE *)Spans + sizeof(Span*)*Width); + warned = false; + + for (x = 0; x < Width; ++x) + { + const column_t *column = (const column_t *)((const BYTE *)patch + LittleLong(patch->columnofs[x])); + int top = -1; + + Spans[x] = spanstuffer; + spanstarter = spanstuffer; + + while (column < maxcol && column->topdelta != 0xFF) + { + if (column->topdelta <= top) + { + top += column->topdelta; + } + else + { + top = column->topdelta; + } + + int len = column->length; + + if (len != 0) + { + if (top + len > Height) // Clip posts that extend past the bottom + { + len = Height - top; + } + if (len > 0) + { + // There is something of this post to draw. If it starts at the same + // place where the previous span ends, add it to that one. If it starts + // before the other one ends, that's bad, but deal with it. If it starts + // after the previous one ends, create another span. + + // Assume we need to create another span. + spanstuffer->TopOffset = top; + spanstuffer->Length = len; + + // Now check if that's really the case. + if (spanstuffer > spanstarter) + { + if ((spanstuffer - 1)->TopOffset + (spanstuffer - 1)->Length == top) + { + (--spanstuffer)->Length += len; + } + else + { + int prevbot; + + while (spanstuffer > spanstarter && + spanstuffer->TopOffset < (prevbot = + (spanstuffer - 1)->TopOffset + (spanstuffer - 1)->Length)) + { + if (spanstuffer->TopOffset < (spanstuffer - 1)->TopOffset) + { + (spanstuffer - 1)->TopOffset = spanstuffer->TopOffset; + } + (spanstuffer - 1)->Length = MAX(prevbot, + spanstuffer->TopOffset + spanstuffer->Length) + - (spanstuffer - 1)->TopOffset; + spanstuffer--; + if (!warned) + { + warned = true; + Printf (PRINT_BOLD, "Patch %s is malformed.\n", Name); + } + } + } + } + spanstuffer++; + } + } + column = (const column_t *)((const BYTE *)column + column->length + 4); + } + + spanstuffer->Length = spanstuffer->TopOffset = 0; + spanstuffer++; + } +} + +// Fix for certain special patches on single-patch textures. +void FPatchTexture::HackHack (int newheight) +{ + BYTE *out; + int x; + + Unload (); + if (Spans != NULL) + { + FreeSpans (Spans); + } + + { + FMemLump lump = Wads.ReadLump (SourceLump); + const patch_t *patch = (const patch_t *)lump.GetMem(); + + Width = LittleShort(patch->width); + Height = newheight; + LeftOffset = 0; + TopOffset = 0; + + Pixels = new BYTE[Width * Height]; + + // Draw the image to the buffer + for (x = 0, out = Pixels; x < Width; ++x) + { + const BYTE *in = (const BYTE *)patch + LittleLong(patch->columnofs[x]) + 3; + + for (int y = newheight; y > 0; --y) + { + *out = *in != 255 ? *in : Near255; + out++, in++; + } + out += newheight; + } + } + + // Create the spans + Spans = (Span **)M_Malloc (sizeof(Span *)*Width + sizeof(Span)*Width*2); + + Span *span = (Span *)&Spans[Width]; + + for (x = 0; x < Width; ++x) + { + Spans[x] = span; + span[0].Length = newheight; + span[0].TopOffset = 0; + span[1].Length = 0; + span[1].TopOffset = 0; + span += 2; + } +} diff --git a/src/textures/pngtexture.cpp b/src/textures/pngtexture.cpp new file mode 100644 index 000000000..3bebcb0c1 --- /dev/null +++ b/src/textures/pngtexture.cpp @@ -0,0 +1,419 @@ +/* +** pmgtexture.cpp +** Texture class for PNG images +** +**--------------------------------------------------------------------------- +** 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 "r_local.h" +#include "w_wad.h" +#include "templates.h" +#include "m_png.h" + + +bool FPNGTexture::Check(FileReader & file) +{ + DWORD id; + file.Seek(0, SEEK_SET); + file.Read(&id, 4); + return (id == MAKE_ID(137,'P','N','G')); +} + +FTexture *FPNGTexture::Create(FileReader & data, int lumpnum) +{ + union + { + DWORD dw; + WORD w[2]; + BYTE b[4]; + } first4bytes; + + DWORD width, height; + BYTE bitdepth, colortype, compression, filter, interlace; + + // This is most likely a PNG, but make sure. (Note that if the + // first 4 bytes match, but later bytes don't, we assume it's + // a corrupt PNG.) + data.Seek(4, SEEK_SET); + data.Read (first4bytes.b, 4); + if (first4bytes.dw != MAKE_ID(13,10,26,10)) return NULL; + data.Read (first4bytes.b, 4); + if (first4bytes.dw != MAKE_ID(0,0,0,13)) return NULL; + data.Read (first4bytes.b, 4); + if (first4bytes.dw != MAKE_ID('I','H','D','R')) return NULL; + + // The PNG looks valid so far. Check the IHDR to make sure it's a + // type of PNG we support. + data >> width >> height + >> bitdepth >> colortype >> compression >> filter >> interlace; + + if (compression != 0 || filter != 0 || interlace > 1) + { + return NULL; + } + if (!((1 << colortype) & 0x5D)) + { + return NULL; + } + if (!((1 << bitdepth) & 0x116)) + { + return NULL; + } + + // Just for completeness, make sure the PNG has something more than an + // IHDR. + data.Seek (4, SEEK_CUR); + data.Read (first4bytes.b, 4); + if (first4bytes.dw == 0) + { + data.Read (first4bytes.b, 4); + if (first4bytes.dw == MAKE_ID('I','E','N','D')) + { + return NULL; + } + } + + return new FPNGTexture (data, lumpnum, BigLong((int)width), BigLong((int)height), + bitdepth, colortype, interlace); +} + +FPNGTexture::FPNGTexture (FileReader &lump, int lumpnum, int width, int height, + BYTE depth, BYTE colortype, BYTE interlace) +: SourceLump(lumpnum), Pixels(0), Spans(0), + BitDepth(depth), ColorType(colortype), Interlace(interlace), + PaletteMap(0), PaletteSize(0), StartOfIDAT(0) +{ + union + { + DWORD palette[256]; + BYTE pngpal[256][3]; + }; + BYTE trans[256]; + bool havetRNS = false; + DWORD len, id; + int i; + + Wads.GetLumpName (Name, lumpnum); + Name[8] = 0; + + UseType = TEX_MiscPatch; + LeftOffset = 0; + TopOffset = 0; + bMasked = false; + + Width = width; + Height = height; + CalcBitSize (); + + memset (trans, 255, 256); + + // Parse pre-IDAT chunks. I skip the CRCs. Is that bad? + lump.Seek (33, SEEK_SET); + + lump >> len >> id; + while (id != MAKE_ID('I','D','A','T') && id != MAKE_ID('I','E','N','D')) + { + len = BigLong((unsigned int)len); + switch (id) + { + default: + lump.Seek (len, SEEK_CUR); + break; + + case MAKE_ID('g','r','A','b'): + // This is like GRAB found in an ILBM, except coordinates use 4 bytes + { + DWORD hotx, hoty; + + lump >> hotx >> hoty; + LeftOffset = BigLong((int)hotx); + TopOffset = BigLong((int)hoty); + } + break; + + case MAKE_ID('P','L','T','E'): + PaletteSize = MIN<int> (len / 3, 256); + lump.Read (pngpal, PaletteSize * 3); + if (PaletteSize * 3 != (int)len) + { + lump.Seek (len - PaletteSize * 3, SEEK_CUR); + } + for (i = PaletteSize - 1; i >= 0; --i) + { + palette[i] = MAKERGB(pngpal[i][0], pngpal[i][1], pngpal[i][2]); + } + break; + + case MAKE_ID('t','R','N','S'): + lump.Read (trans, len); + havetRNS = true; + break; + + case MAKE_ID('a','l','P','h'): + bAlphaTexture = true; + bMasked = true; + break; + } + lump >> len >> len; // Skip CRC + id = MAKE_ID('I','E','N','D'); + lump >> id; + } + StartOfIDAT = lump.Tell() - 8; + + switch (colortype) + { + case 4: // Grayscale + Alpha + bMasked = true; + // intentional fall-through + + case 0: // Grayscale + if (!bAlphaTexture) + { + if (colortype == 0 && havetRNS && trans[0] != 0) + { + bMasked = true; + PaletteSize = 256; + PaletteMap = new BYTE[256]; + memcpy (PaletteMap, GrayMap, 256); + PaletteMap[trans[0]] = 0; + } + else + { + PaletteMap = GrayMap; + } + } + break; + + case 3: // Paletted + PaletteMap = new BYTE[PaletteSize]; + GPalette.MakeRemap (palette, PaletteMap, trans, PaletteSize); + for (i = 0; i < PaletteSize; ++i) + { + if (trans[i] == 0) + { + bMasked = true; + PaletteMap[i] = 0; + } + } + break; + + case 6: // RGB + Alpha + bMasked = true; + break; + } +} + +FPNGTexture::~FPNGTexture () +{ + Unload (); + if (Spans != NULL) + { + FreeSpans (Spans); + Spans = NULL; + } + if (PaletteMap != NULL && PaletteMap != GrayMap) + { + delete[] PaletteMap; + PaletteMap = NULL; + } +} + +void FPNGTexture::Unload () +{ + if (Pixels != NULL) + { + delete[] Pixels; + Pixels = NULL; + } +} + +const BYTE *FPNGTexture::GetColumn (unsigned int column, const Span **spans_out) +{ + if (Pixels == NULL) + { + MakeTexture (); + } + if ((unsigned)column >= (unsigned)Width) + { + if (WidthMask + 1 == Width) + { + column &= WidthMask; + } + else + { + column %= Width; + } + } + if (spans_out != NULL) + { + *spans_out = Spans[column]; + } + return Pixels + column*Height; +} + +const BYTE *FPNGTexture::GetPixels () +{ + if (Pixels == NULL) + { + MakeTexture (); + } + return Pixels; +} + +void FPNGTexture::MakeTexture () +{ + FWadLump lump = Wads.OpenLumpNum (SourceLump); + + Pixels = new BYTE[Width*Height]; + if (StartOfIDAT == 0) + { + memset (Pixels, 0x99, Width*Height); + } + else + { + DWORD len, id; + lump.Seek (StartOfIDAT, SEEK_SET); + lump >> len >> id; + + if (ColorType == 0 || ColorType == 3) /* Grayscale and paletted */ + { + M_ReadIDAT (&lump, Pixels, Width, Height, Width, BitDepth, ColorType, Interlace, BigLong((unsigned int)len)); + + if (Width == Height) + { + if (PaletteMap != NULL) + { + FlipSquareBlockRemap (Pixels, Width, Height, PaletteMap); + } + else + { + FlipSquareBlock (Pixels, Width, Height); + } + } + else + { + BYTE *newpix = new BYTE[Width*Height]; + if (PaletteMap != NULL) + { + FlipNonSquareBlockRemap (newpix, Pixels, Width, Height, PaletteMap); + } + else + { + FlipNonSquareBlock (newpix, Pixels, Width, Height, Width); + } + BYTE *oldpix = Pixels; + Pixels = newpix; + delete[] oldpix; + } + } + else /* RGB and/or Alpha present */ + { + int bytesPerPixel = ColorType == 2 ? 3 : ColorType == 4 ? 2 : 4; + BYTE *tempix = new BYTE[Width * Height * bytesPerPixel]; + BYTE *in, *out; + int x, y, pitch, backstep; + + M_ReadIDAT (&lump, tempix, Width, Height, Width*bytesPerPixel, BitDepth, ColorType, Interlace, BigLong((unsigned int)len)); + in = tempix; + out = Pixels; + + // Convert from source format to paletted, column-major. + // Formats with alpha maps are reduced to only 1 bit of alpha. + switch (ColorType) + { + case 2: // RGB + pitch = Width * 3; + backstep = Height * pitch - 3; + for (x = Width; x > 0; --x) + { + for (y = Height; y > 0; --y) + { + *out++ = RGB32k[in[0]>>3][in[1]>>3][in[2]>>3]; + in += pitch; + } + in -= backstep; + } + break; + + case 4: // Grayscale + Alpha + pitch = Width * 2; + backstep = Height * pitch - 2; + if (PaletteMap != NULL) + { + for (x = Width; x > 0; --x) + { + for (y = Height; y > 0; --y) + { + *out++ = in[1] < 128 ? 0 : PaletteMap[in[0]]; + in += pitch; + } + in -= backstep; + } + } + else + { + for (x = Width; x > 0; --x) + { + for (y = Height; y > 0; --y) + { + *out++ = in[1] < 128 ? 0 : in[0]; + in += pitch; + } + in -= backstep; + } + } + break; + + case 6: // RGB + Alpha + pitch = Width * 4; + backstep = Height * pitch - 4; + for (x = Width; x > 0; --x) + { + for (y = Height; y > 0; --y) + { + *out++ = in[3] < 128 ? 0 : RGB32k[in[0]>>3][in[1]>>3][in[2]>>3]; + in += pitch; + } + in -= backstep; + } + break; + } + delete[] tempix; + } + } + if (Spans == NULL) + { + Spans = CreateSpans (Pixels); + } +} + diff --git a/src/textures/rawpagetexture.cpp b/src/textures/rawpagetexture.cpp new file mode 100644 index 000000000..4cb80f639 --- /dev/null +++ b/src/textures/rawpagetexture.cpp @@ -0,0 +1,201 @@ +/* +** rawpagetexture.cpp +** Texture class for Raven's raw fullscreen pages +** +**--------------------------------------------------------------------------- +** 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 "r_data.h" +#include "w_wad.h" + + + +bool FRawPageTexture::Check(FileReader & data) +{ + if (data.GetLength() != 64000) return false; + + // This is probably a raw page graphic, but do some checking to be sure + patch_t *foo; + int height; + int width; + + foo = (patch_t *)M_Malloc (data.GetLength()); + data.Seek (0, SEEK_SET); + data.Read (foo, data.GetLength()); + + height = LittleShort(foo->height); + width = LittleShort(foo->width); + + if (height > 0 && height < 510 && width > 0 && width < 15997) + { + // 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) + { + DWORD ofs = LittleLong(foo->columnofs[x]); + if (ofs == (DWORD)width * 4 + 8) + { + gapAtStart = false; + } + else if (ofs >= 64000-1) // Need one byte for an empty column + { + free (foo); + return true; + } + else + { + // Ensure this column does not extend beyond the end of the patch + const BYTE *foo2 = (const BYTE *)foo; + while (ofs < 64000) + { + if (foo2[ofs] == 255) + { + free (foo); + return true; + } + ofs += foo2[ofs+1] + 4; + } + if (ofs >= 64000) + { + free (foo); + return true; + } + } + } + if (gapAtStart || (x != width)) + { + free (foo); + return true; + } + free(foo); + return false; + } + else + { + free (foo); + return true; + } +} + +FTexture *FRawPageTexture::Create(FileReader & file, int lumpnum) +{ + return new FRawPageTexture(lumpnum); +} + + +const FTexture::Span FRawPageTexture::DummySpans[2] = +{ + { 0, 200 }, { 0, 0 } +}; + +FRawPageTexture::FRawPageTexture (int lumpnum) +: SourceLump(lumpnum), Pixels(0) +{ + Wads.GetLumpName (Name, lumpnum); + Name[8] = 0; + + Width = 320; + Height = 200; + WidthBits = 8; + HeightBits = 8; + WidthMask = 255; +} + +FRawPageTexture::~FRawPageTexture () +{ + Unload (); +} + +void FRawPageTexture::Unload () +{ + if (Pixels != NULL) + { + delete[] Pixels; + Pixels = NULL; + } +} + +const BYTE *FRawPageTexture::GetColumn (unsigned int column, const Span **spans_out) +{ + if (Pixels == NULL) + { + MakeTexture (); + } + if ((unsigned)column >= (unsigned)Width) + { + column %= 320; + } + if (spans_out != NULL) + { + *spans_out = DummySpans; + } + return Pixels + column*Height; +} + +const BYTE *FRawPageTexture::GetPixels () +{ + if (Pixels == NULL) + { + MakeTexture (); + } + return Pixels; +} + +void FRawPageTexture::MakeTexture () +{ + FMemLump lump = Wads.ReadLump (SourceLump); + const BYTE *source = (const BYTE *)lump.GetMem(); + const BYTE *source_p = source; + BYTE *dest_p; + + Pixels = new BYTE[Width*Height]; + dest_p = Pixels; + + // Convert the source image from row-major to column-major format + for (int y = 200; y != 0; --y) + { + for (int x = 320; x != 0; --x) + { + *dest_p = GPalette.Remap[*source_p]; + dest_p += 200; + source_p++; + } + dest_p -= 200*320-1; + } +} + diff --git a/src/textures/texture.cpp b/src/textures/texture.cpp new file mode 100644 index 000000000..81674fd9a --- /dev/null +++ b/src/textures/texture.cpp @@ -0,0 +1,423 @@ +/* +** texture.cpp +** The base texture class +** +**--------------------------------------------------------------------------- +** 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 "r_data.h" +#include "templates.h" + +typedef bool (*CheckFunc)(FileReader & file); +typedef FTexture * (*CreateFunc)(FileReader & file, int lumpnum); + +struct TexCreateInfo +{ + CheckFunc Check; + CreateFunc Create; + int usetype; +}; + +BYTE FTexture::GrayMap[256]; + +void FTexture::InitGrayMap() +{ + for (int i = 0; i < 256; ++i) + { + GrayMap[i] = ColorMatcher.Pick (i, i, i); + } +} + +// Examines the lump contents to decide what type of texture to create, +// and creates the texture. +FTexture * FTexture::CreateTexture (int lumpnum, int usetype) +{ + static TexCreateInfo CreateInfo[]={ + { FIMGZTexture::Check, FIMGZTexture::Create, FTexture::TEX_Any }, + { FPNGTexture::Check, FPNGTexture::Create, FTexture::TEX_Any }, + { FJPEGTexture::Check, FJPEGTexture::Create, FTexture::TEX_Any }, + { FTGATexture::Check, FTGATexture::Create, FTexture::TEX_Any }, + { FRawPageTexture::Check, FRawPageTexture::Create, FTexture::TEX_MiscPatch }, + { FFlatTexture::Check, FFlatTexture::Create, FTexture::TEX_Flat }, + { FPatchTexture::Check, FPatchTexture::Create, FTexture::TEX_Any }, + { FAutomapTexture::Check, FAutomapTexture::Create, FTexture::TEX_Autopage }, + }; + + + FWadLump data = Wads.OpenLumpNum (lumpnum); + + for(int i = 0; i < countof(CreateInfo); i++) + { + if ((CreateInfo[i].usetype == usetype || CreateInfo[i].usetype == TEX_Any) && + CreateInfo[i].Check(data)) + { + FTexture * tex = CreateInfo[i].Create(data, lumpnum); + if (tex != NULL) + { + tex->UseType = usetype; + if (usetype == FTexture::TEX_Flat) + { + int w = tex->GetWidth(); + int h = tex->GetHeight(); + + // Auto-scale flats with dimensions 128x128 and 256x256 + if (w==128 && h==128) tex->ScaleX = tex->ScaleY = 16; + else if (w==256 && h==256) tex->ScaleX = tex->ScaleY = 32; + } + return tex; + } + } + } + return NULL; +} + +FTexture::FTexture () +: LeftOffset(0), TopOffset(0), + WidthBits(0), HeightBits(0), ScaleX(8), ScaleY(8), + UseType(TEX_Any), bNoDecals(false), bNoRemap0(false), bWorldPanning(false), + bMasked(true), bAlphaTexture(false), bHasCanvas(false), bWarped(0), bIsPatch(false), + Rotations(0xFFFF), Width(0), Height(0), WidthMask(0) +{ + *Name=0; +} + +FTexture::~FTexture () +{ +} + +bool FTexture::CheckModified () +{ + return false; +} + +void FTexture::SetFrontSkyLayer () +{ + bNoRemap0 = true; +} + +void FTexture::CalcBitSize () +{ + // WidthBits is rounded down, and HeightBits is rounded up + int i; + + for (i = 0; (1 << i) < Width; ++i) + { } + + WidthBits = i; + + // Having WidthBits that would allow for columns past the end of the + // texture is not allowed, even if it means the entire texture is + // not drawn. + if (Width < (1 << WidthBits)) + { + WidthBits--; + } + WidthMask = (1 << WidthBits) - 1; + + // The minimum height is 2, because we cannot shift right 32 bits. + for (i = 1; (1 << i) < Height; ++i) + { } + + HeightBits = i; +} + +FTexture::Span **FTexture::CreateSpans (const BYTE *pixels) const +{ + Span **spans, *span; + + if (!bMasked) + { // Texture does not have holes, so it can use a simpler span structure + spans = (Span **)M_Malloc (sizeof(Span*)*Width + sizeof(Span)*2); + span = (Span *)&spans[Width]; + for (int x = 0; x < Width; ++x) + { + spans[x] = span; + } + span[0].Length = Height; + span[0].TopOffset = 0; + span[1].Length = 0; + span[1].TopOffset = 0; + } + else + { // Texture might have holes, so build a complete span structure + int numcols = Width; + int numrows = Height; + int numspans = numcols; // One span to terminate each column + const BYTE *data_p; + bool newspan; + int x, y; + + data_p = pixels; + + // Count the number of spans in this texture + for (x = numcols; x > 0; --x) + { + newspan = true; + for (y = numrows; y > 0; --y) + { + if (*data_p++ == 0) + { + if (!newspan) + { + newspan = true; + } + } + else if (newspan) + { + newspan = false; + numspans++; + } + } + } + + // Allocate space for the spans + spans = (Span **)M_Malloc (sizeof(Span*)*numcols + sizeof(Span)*numspans); + + // Fill in the spans + for (x = 0, span = (Span *)&spans[numcols], data_p = pixels; x < numcols; ++x) + { + newspan = true; + spans[x] = span; + for (y = 0; y < numrows; ++y) + { + if (*data_p++ == 0) + { + if (!newspan) + { + newspan = true; + span++; + } + } + else + { + if (newspan) + { + newspan = false; + span->TopOffset = y; + span->Length = 1; + } + else + { + span->Length++; + } + } + } + if (!newspan) + { + span++; + } + span->TopOffset = 0; + span->Length = 0; + span++; + } + } + return spans; +} + +void FTexture::FreeSpans (Span **spans) const +{ + free (spans); +} + +void FTexture::CopyToBlock (BYTE *dest, int dwidth, int dheight, int xpos, int ypos, const BYTE *translation) +{ + int x1 = xpos, x2 = x1 + GetWidth(), xo = -x1; + + if (x1 < 0) + { + x1 = 0; + } + if (x2 > dwidth) + { + x2 = dwidth; + } + for (; x1 < x2; ++x1) + { + const BYTE *data; + const Span *span; + BYTE *outtop = &dest[dheight * x1]; + + data = GetColumn (x1 + xo, &span); + + while (span->Length != 0) + { + int len = span->Length; + int y = ypos + span->TopOffset; + int adv = span->TopOffset; + + if (y < 0) + { + adv -= y; + len += y; + y = 0; + } + if (y + len > dheight) + { + len = dheight - y; + } + if (len > 0) + { + if (translation == NULL) + { + memcpy (outtop + y, data + adv, len); + } + else + { + for (int j = 0; j < len; ++j) + { + outtop[y+j] = translation[data[adv+j]]; + } + } + } + span++; + } + } +} + +// Converts a texture between row-major and column-major format +// by flipping it about the X=Y axis. + +void FTexture::FlipSquareBlock (BYTE *block, int x, int y) +{ + int i, j; + + if (x != y) return; + + for (i = 0; i < x; ++i) + { + BYTE *corner = block + x*i + i; + int count = x - i; + if (count & 1) + { + count--; + swap<BYTE> (corner[count], corner[count*x]); + } + for (j = 0; j < count; j += 2) + { + swap<BYTE> (corner[j], corner[j*x]); + swap<BYTE> (corner[j+1], corner[(j+1)*x]); + } + } +} + +void FTexture::FlipSquareBlockRemap (BYTE *block, int x, int y, const BYTE *remap) +{ + int i, j; + BYTE t; + + if (x != y) return; + + for (i = 0; i < x; ++i) + { + BYTE *corner = block + x*i + i; + int count = x - i; + if (count & 1) + { + count--; + t = remap[corner[count]]; + corner[count] = remap[corner[count*x]]; + corner[count*x] = t; + } + for (j = 0; j < count; j += 2) + { + t = remap[corner[j]]; + corner[j] = remap[corner[j*x]]; + corner[j*x] = t; + t = remap[corner[j+1]]; + corner[j+1] = remap[corner[(j+1)*x]]; + corner[(j+1)*x] = t; + } + } +} + +void FTexture::FlipNonSquareBlock (BYTE *dst, const BYTE *src, int x, int y, int srcpitch) +{ + int i, j; + + for (i = 0; i < x; ++i) + { + for (j = 0; j < y; ++j) + { + dst[i*y+j] = src[i+j*srcpitch]; + } + } +} + +void FTexture::FlipNonSquareBlockRemap (BYTE *dst, const BYTE *src, int x, int y, const BYTE *remap) +{ + int i, j; + + for (i = 0; i < x; ++i) + { + for (j = 0; j < y; ++j) + { + dst[i*y+j] = remap[src[i+j*x]]; + } + } +} + +FDummyTexture::FDummyTexture () +{ + Width = 64; + Height = 64; + HeightBits = 6; + WidthBits = 6; + WidthMask = 63; + Name[0] = 0; + UseType = TEX_Null; +} + +void FDummyTexture::Unload () +{ +} + +void FDummyTexture::SetSize (int width, int height) +{ + Width = width; + Height = height; + CalcBitSize (); +} + +// This must never be called +const BYTE *FDummyTexture::GetColumn (unsigned int column, const Span **spans_out) +{ + return NULL; +} + +// And this also must never be called +const BYTE *FDummyTexture::GetPixels () +{ + return NULL; +} + diff --git a/src/textures/tgatexture.cpp b/src/textures/tgatexture.cpp new file mode 100644 index 000000000..e563e5449 --- /dev/null +++ b/src/textures/tgatexture.cpp @@ -0,0 +1,398 @@ +/* +** pmgtexture.cpp +** Texture class for TGA images +** +**--------------------------------------------------------------------------- +** Copyright 2006 Christoph Oelckers +** 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 "r_local.h" +#include "w_wad.h" +#include "templates.h" + + + +#pragma pack(1) + +struct TGAHeader +{ + BYTE id_len; + BYTE has_cm; + BYTE img_type; + SWORD cm_first; + SWORD cm_length; + BYTE cm_size; + + SWORD x_origin; + SWORD y_origin; + SWORD width; + SWORD height; + BYTE bpp; + BYTE img_desc; +}; + +#pragma pack() + +bool FTGATexture::Check(FileReader & data) +{ + + TGAHeader hdr; + + if (data.GetLength() < sizeof(hdr)) return false; + + data.Seek(0, SEEK_SET); + data.Read(&hdr, sizeof(hdr)); +#ifdef WORDS_BIGENDIAN + hdr.width = LittleShort(hdr.width); + hdr.height = LittleShort(hdr.height); +#endif + + // Not much that can be done here because TGA does not have a proper + // header to be identified with. + if (hdr.has_cm != 0 && hdr.has_cm != 1) return false; + if (hdr.width <=0 || hdr.height <=0 || hdr.width > 2048 || hdr.height > 2048) return false; + if (hdr.bpp != 8 && hdr.bpp != 15 && hdr.bpp != 16 && hdr.bpp !=24 && hdr.bpp !=32) return false; + if (hdr.img_type <= 0 || hdr.img_type > 11) return false; + if (hdr.img_type >=4 && hdr.img_type <= 8) return false; + if ((hdr.img_desc & 16) != 0) return false; + return true; +} + +FTexture *FTGATexture::Create(FileReader & file, int lumpnum) +{ + TGAHeader hdr; + file.Seek(0, SEEK_SET); + file.Read(&hdr, sizeof(hdr)); + + return new FTGATexture(lumpnum, LittleShort(hdr.width), LittleShort(hdr.height)); +} + + +FTGATexture::FTGATexture (int lumpnum, int w, int h) +: SourceLump(lumpnum), Pixels(0), Spans(0) +{ + Wads.GetLumpName (Name, lumpnum); + Name[8] = 0; + Width = w; + Height = h; + CalcBitSize(); +} + +FTGATexture::~FTGATexture () +{ + Unload (); + if (Spans != NULL) + { + FreeSpans (Spans); + Spans = NULL; + } +} + + +void FTGATexture::Unload () +{ + if (Pixels != NULL) + { + delete[] Pixels; + Pixels = NULL; + } +} + +const BYTE *FTGATexture::GetColumn (unsigned int column, const Span **spans_out) +{ + if (Pixels == NULL) + { + MakeTexture (); + } + if ((unsigned)column >= (unsigned)Width) + { + if (WidthMask + 1 == Width) + { + column &= WidthMask; + } + else + { + column %= Width; + } + } + if (spans_out != NULL) + { + *spans_out = Spans[column]; + } + return Pixels + column*Height; +} + +const BYTE *FTGATexture::GetPixels () +{ + if (Pixels == NULL) + { + MakeTexture (); + } + return Pixels; +} + +void FTGATexture::ReadCompressed(FileReader &lump, BYTE * buffer, int bytesperpixel) +{ + BYTE b; + BYTE data[4]; + int Size = Width * Height; + + while (Size > 0) + { + lump >> b; + if (b & 128) + { + b&=~128; + lump.Read(data, bytesperpixel); + for (int i=MIN<int>(Size, (b+1)); i>0; i--) + { + buffer[0] = data[0]; + if (bytesperpixel>=2) buffer[1] = data[1]; + if (bytesperpixel>=3) buffer[2] = data[2]; + if (bytesperpixel==4) buffer[3] = data[3]; + buffer+=bytesperpixel; + } + } + else + { + lump.Read(buffer, MIN<int>(Size, (b+1))*bytesperpixel); + buffer += (b+1)*bytesperpixel; + } + Size -= b+1; + } +} + +void FTGATexture::MakeTexture () +{ + BYTE PaletteMap[256]; + FWadLump lump = Wads.OpenLumpNum (SourceLump); + TGAHeader hdr; + WORD w; + BYTE r,g,b,a; + BYTE * buffer; + + Pixels = new BYTE[Width*Height]; + lump.Read(&hdr, sizeof(hdr)); + lump.Seek(hdr.id_len, SEEK_CUR); + +#ifdef WORDS_BIGENDIAN + hdr.width = LittleShort(hdr.width); + hdr.height = LittleShort(hdr.height); + hdr.cm_first = LittleShort(hdr.cm_first); + hdr.cm_length = LittleShort(hdr.cm_length); +#endif + + bMasked = false; + if (hdr.has_cm) + { + memset(PaletteMap, 0, 256); + for (int i = hdr.cm_first; i < hdr.cm_first + hdr.cm_length && i < 256; i++) + { + switch (hdr.cm_size) + { + case 15: + case 16: + lump >> w; + r = (w & 0x001F) << 3; + g = (w & 0x03E0) >> 2; + b = (w & 0x7C00) >> 7; + a = 255; + break; + + case 24: + lump >> b >> g >> r; + a=255; + break; + + case 32: + lump >> b >> g >> r >> a; + if ((hdr.img_desc&15)!=8) a=255; + else if (a<128) bMasked=true; + break; + + default: // should never happen + r=g=b=a=0; + break; + } + PaletteMap[i] = a>=128? ColorMatcher.Pick(r, g, b) : 0; + } + } + + int Size = Width * Height * (hdr.bpp>>3); + buffer = new BYTE[Size]; + + if (hdr.img_type < 4) // uncompressed + { + lump.Read(buffer, Size); + } + else // compressed + { + ReadCompressed(lump, buffer, hdr.bpp>>3); + } + + BYTE * ptr = buffer; + int step_x = (hdr.bpp>>3); + int Pitch = Width * step_x; + + if (hdr.img_desc&32) + { + ptr += (Width-1) * step_x; + step_x =- step_x; + } + if (!(hdr.img_desc&64)) + { + ptr += (Height-1) * Pitch; + Pitch = -Pitch; + } + + switch (hdr.img_type & 7) + { + case 1: // paletted + for(int y=0;y<Height;y++) + { + BYTE * p = ptr + y * Pitch; + for(int x=0;x<Width;x++) + { + Pixels[x*Height+y] = PaletteMap[*p]; + p+=step_x; + } + } + break; + + case 2: // RGB + switch (hdr.bpp) + { + case 15: + case 16: + step_x>>=1; + for(int y=0;y<Height;y++) + { + WORD * p = (WORD*)(ptr + y * Pitch); + for(int x=0;x<Width;x++) + { + int v = LittleLong(*p); + Pixels[x*Height+y] = RGB32k[(v>>10) & 0x1f][(v>>5) & 0x1f][v & 0x1f]; + p+=step_x; + } + } + break; + + case 24: + for(int y=0;y<Height;y++) + { + BYTE * p = ptr + y * Pitch; + for(int x=0;x<Width;x++) + { + Pixels[x*Height+y] = RGB32k[p[2]>>3][p[1]>>3][p[0]>>3]; + p+=step_x; + } + } + break; + + case 32: + if ((hdr.img_desc&15)!=8) // 32 bits without a valid alpha channel + { + for(int y=0;y<Height;y++) + { + BYTE * p = ptr + y * Pitch; + for(int x=0;x<Width;x++) + { + Pixels[x*Height+y] = RGB32k[p[2]>>3][p[1]>>3][p[0]>>3]; + p+=step_x; + } + } + } + else + { + bMasked=true; + + for(int y=0;y<Height;y++) + { + BYTE * p = ptr + y * Pitch; + for(int x=0;x<Width;x++) + { + Pixels[x*Height+y] = p[3] >= 128? RGB32k[p[2]>>3][p[1]>>3][p[0]>>3] : 0; + p+=step_x; + } + } + } + break; + + default: + break; + } + break; + + case 3: // Grayscale + switch (hdr.bpp) + { + case 8: + for(int y=0;y<Height;y++) + { + BYTE * p = ptr + y * Pitch; + for(int x=0;x<Width;x++) + { + Pixels[x*Height+y] = GrayMap[*p]; + p+=step_x; + } + } + break; + + case 16: + for(int y=0;y<Height;y++) + { + BYTE * p = ptr + y * Pitch; + for(int x=0;x<Width;x++) + { + Pixels[x*Height+y] = GrayMap[p[1]]; // only use the high byte + p+=step_x; + } + } + break; + + default: + break; + } + break; + + default: + break; + } + delete [] buffer; + if (Spans == NULL) + { + Spans = CreateSpans (Pixels); + } +} + + + diff --git a/src/textures/warptexture.cpp b/src/textures/warptexture.cpp new file mode 100644 index 000000000..d87e3e737 --- /dev/null +++ b/src/textures/warptexture.cpp @@ -0,0 +1,233 @@ +/* +** warptexture.cpp +** Texture class for warped textures +** +**--------------------------------------------------------------------------- +** 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 "r_data.h" +#include "templates.h" + + +FWarpTexture::FWarpTexture (FTexture *source) +: SourcePic (source), Pixels (0), Spans (0), GenTime (0) +{ + Width = source->GetWidth (); + Height = source->GetHeight (); + LeftOffset = source->LeftOffset; + TopOffset = source->TopOffset; + WidthBits = source->WidthBits; + HeightBits = source->HeightBits; + WidthMask = (1 << WidthBits) - 1; + ScaleX = source->ScaleX; + ScaleY = source->ScaleY; + bNoDecals = source->bNoDecals; + Rotations = source->Rotations; + bWarped = 1; +} + +FWarpTexture::~FWarpTexture () +{ + Unload (); + if (Spans != NULL) + { + FreeSpans (Spans); + Spans = NULL; + } + delete SourcePic; +} + +void FWarpTexture::Unload () +{ + if (Pixels != NULL) + { + delete[] Pixels; + Pixels = NULL; + } + if (Spans != NULL) + { + FreeSpans (Spans); + Spans = NULL; + } + SourcePic->Unload (); +} + +bool FWarpTexture::CheckModified () +{ + return r_FrameTime != GenTime; +} + +const BYTE *FWarpTexture::GetPixels () +{ + DWORD time = r_FrameTime; + + if (Pixels == NULL || time != GenTime) + { + MakeTexture (time); + } + return Pixels; +} + +const BYTE *FWarpTexture::GetColumn (unsigned int column, const Span **spans_out) +{ + DWORD time = r_FrameTime; + + if (Pixels == NULL || time != GenTime) + { + MakeTexture (time); + } + if ((unsigned)column >= (unsigned)Width) + { + if (WidthMask + 1 == Width) + { + column &= WidthMask; + } + else + { + column %= Width; + } + } + if (spans_out != NULL) + { + if (Spans == NULL) + { + Spans = CreateSpans (Pixels); + } + *spans_out = Spans[column]; + } + return Pixels + column*Height; +} + +void FWarpTexture::MakeTexture (DWORD time) +{ + const BYTE *otherpix = SourcePic->GetPixels (); + + if (Pixels == NULL) + { + Pixels = new BYTE[Width * Height]; + } + if (Spans != NULL) + { + FreeSpans (Spans); + Spans = NULL; + } + + GenTime = time; + + byte *buffer = (byte *)alloca (MAX (Width, Height)); + int xsize = Width; + int ysize = Height; + int xmask = WidthMask; + int ymask = Height - 1; + int ybits = HeightBits; + int x, y; + + if ((1 << ybits) > Height) + { + ybits--; + } + + DWORD timebase = time * 32 / 28; + for (y = ysize-1; y >= 0; y--) + { + int xt, xf = (finesine[(timebase+y*128)&FINEMASK]>>13) & xmask; + const BYTE *source = otherpix + y; + BYTE *dest = Pixels + y; + for (xt = xsize; xt; xt--, xf = (xf+1)&xmask, dest += ysize) + *dest = source[xf << ybits]; + } + timebase = time * 23 / 28; + for (x = xsize-1; x >= 0; x--) + { + int yt, yf = (finesine[(time+(x+17)*128)&FINEMASK]>>13) & ymask; + const BYTE *source = Pixels + (x << ybits); + BYTE *dest = buffer; + for (yt = ysize; yt; yt--, yf = (yf+1)&ymask) + *dest++ = source[yf]; + memcpy (Pixels+(x<<ybits), buffer, ysize); + } +} + +// [GRB] Eternity-like warping +FWarp2Texture::FWarp2Texture (FTexture *source) +: FWarpTexture (source) +{ + bWarped = 2; +} + +void FWarp2Texture::MakeTexture (DWORD time) +{ + const BYTE *otherpix = SourcePic->GetPixels (); + + if (Pixels == NULL) + { + Pixels = new BYTE[Width * Height]; + } + if (Spans != NULL) + { + FreeSpans (Spans); + Spans = NULL; + } + + GenTime = time; + + int xsize = Width; + int ysize = Height; + int xmask = WidthMask; + int ymask = Height - 1; + int ybits = HeightBits; + int x, y; + + if ((1 << ybits) > Height) + { + ybits--; + } + + DWORD timebase = time * 40 / 28; + for (x = xsize-1; x >= 0; x--) + { + for (y = ysize-1; y >= 0; y--) + { + int xt = (x + 128 + + ((finesine[(y*128 + timebase*5 + 900) & FINEMASK]*2)>>FRACBITS) + + ((finesine[(x*256 + timebase*4 + 300) & FINEMASK]*2)>>FRACBITS)) & xmask; + int yt = (y + 128 + + ((finesine[(y*128 + timebase*3 + 700) & FINEMASK]*2)>>FRACBITS) + + ((finesine[(x*256 + timebase*4 + 1200) & FINEMASK]*2)>>FRACBITS)) & ymask; + const BYTE *source = otherpix + (xt << ybits) + yt; + BYTE *dest = Pixels + (x << ybits) + y; + *dest = *source; + } + } +} + diff --git a/thingdef_specials.gperf b/thingdef_specials.gperf index 5646a2869..637c244a9 100644 --- a/thingdef_specials.gperf +++ b/thingdef_specials.gperf @@ -77,7 +77,7 @@ teleport,70,1 teleport_nofog,71,1,2 teleport_newmap,74,2 teleport_endgame,75,0 -thrustthing,72,2,3 +thrustthing,72,2,4 damagething,73,1 thing_activate,130,1 thing_deactivate,131,1 @@ -168,4 +168,13 @@ floorandceiling_lowerraise,251,3 ceiling_raisetonearest,252,2 ceiling_lowertolowest,253,2 ceiling_lowertofloor,254,2 -ceiling_crushraiseandstaysila,255,4 \ No newline at end of file +ceiling_crushraiseandstaysila,255,4 +door_animated,14,3 +clearforcefield,34,1 +teleport_zombiechanger,39,2 +acs_executewithresult,84,1,4 +plat_upnearestwaitdownstay,172,3 +noisealert,173,2 +thing_raise,17,1 +startconversation,18,1,2 +acs_lockedexecutedoor,85,5 diff --git a/zdoom.vcproj b/zdoom.vcproj index c1780ed66..f4c19b5e6 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -4334,6 +4334,62 @@ > </File> </Filter> + <Filter + Name="Textures" + > + <File + RelativePath=".\src\textures\automaptexture.cpp" + > + </File> + <File + RelativePath=".\src\textures\buildtexture.cpp" + > + </File> + <File + RelativePath=".\src\textures\canvastexture.cpp" + > + </File> + <File + RelativePath=".\src\textures\flattexture.cpp" + > + </File> + <File + RelativePath=".\src\textures\imgztexture.cpp" + > + </File> + <File + RelativePath=".\src\textures\jpegtexture.cpp" + > + </File> + <File + RelativePath=".\src\textures\multipatchtexture.cpp" + > + </File> + <File + RelativePath=".\src\textures\patchtexture.cpp" + > + </File> + <File + RelativePath=".\src\textures\pngtexture.cpp" + > + </File> + <File + RelativePath=".\src\textures\rawpagetexture.cpp" + > + </File> + <File + RelativePath=".\src\textures\texture.cpp" + > + </File> + <File + RelativePath=".\src\textures\tgatexture.cpp" + > + </File> + <File + RelativePath=".\src\textures\warptexture.cpp" + > + </File> + </Filter> </Filter> <Filter Name="A Header Files"