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 (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 #include #include -#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 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(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 (corner[count], corner[count*x]); - } - for (j = 0; j < count; j += 2) - { - swap (corner[j], corner[j*x]); - swap (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 (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<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 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 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(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 (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 (corner[count], corner[count*x]); + } + for (j = 0; j < count; j += 2) + { + swap (corner[j], corner[j*x]); + swap (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(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(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>=1; + for(int y=0;y>10) & 0x1f][(v>>5) & 0x1f][v & 0x1f]; + p+=step_x; + } + } + break; + + case 24: + for(int y=0;y>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>3][p[1]>>3][p[0]>>3]; + p+=step_x; + } + } + } + else + { + bMasked=true; + + for(int y=0;y= 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;yGetWidth (); + 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<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 @@ > + + + + + + + + + + + + + + + + + + + + + + + + + + + +