- added stripped down versions of GZDoom's texture classes

We need something more manageable to deal with the textures - and the hightile code in particular needs a better backend to read the images.
This commit is contained in:
Christoph Oelckers 2019-10-05 19:28:05 +02:00
parent bedfc262c4
commit 1a5e64329f
25 changed files with 6137 additions and 6 deletions

6
.gitignore vendored
View file

@ -2,11 +2,6 @@
obj/
eduke32
mapster32
voidsw
voidsw-editor
kenbuild
kenbuild-editor
apps/
*.exe
/*.dll
@ -25,7 +20,6 @@ apps/
*.log
*.cache
*.cfg
textures
texturecache
*.index

View file

@ -684,6 +684,8 @@ file( GLOB HEADER_FILES
#sw/src/*.h Shadow Warrior does not work yet.
thirdparty/include/*.h
thirdparty/include/*.hpp
common/textures/*.h
common/textures/formats/*.h
)
@ -967,6 +969,20 @@ set (PCH_SOURCES
common/utility/file_zip.cpp
common/utility/resourcefile.cpp
common/utility/matrix.cpp
common/utility/m_png.cpp
common/utility/memarena.cpp
common/textures/bitmap.cpp
common/textures/texture.cpp
common/textures/image.cpp
common/textures/imagetexture.cpp
common/textures/formats/buildtexture.cpp
common/textures/formats/ddstexture.cpp
common/textures/formats/jpegtexture.cpp
common/textures/formats/pcxtexture.cpp
common/textures/formats/pngtexture.cpp
common/textures/formats/tgatexture.cpp
)
if( MSVC )
@ -1023,6 +1039,7 @@ include_directories(
common
common/utility
common/console
common/textures
platform
${CMAKE_BINARY_DIR}/libraries/gdtoa
@ -1123,6 +1140,8 @@ install(TARGETS demolition
COMPONENT "Game executable")
source_group("Utility" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/utility/.+")
source_group("Code\\Textures" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/textures/.+")
source_group("Code\\Textures\\Formats" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/textures/formats/.+")
source_group("Utility\\Audiolib" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/audiolib/.+")
source_group("Utility\\Audiolib Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/audiolib/include/.+")
source_group("Utility\\Audiolib Sources" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/audiolib/src/.+")

View file

@ -0,0 +1,142 @@
/*
** bitmap.cpp
**
**---------------------------------------------------------------------------
** Copyright 2008 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 <assert.h>
#include "bitmap.h"
//===========================================================================
//
// multi-format pixel copy with colormap application
// requires the previously defined conversion classes to work
//
//===========================================================================
template<class TSrc, class TDest, class TBlend>
void iCopyColors(uint8_t *pout, const uint8_t *pin, int count, int step,
uint8_t tr, uint8_t tg, uint8_t tb)
{
int i;
int a;
for(i=0;i<count;i++)
{
a = TSrc::A(pin, tr, tg, tb);
if (TBlend::ProcessAlpha0() || a)
{
TBlend::OpC(pout[TDest::RED], TSrc::R(pin), a);
TBlend::OpC(pout[TDest::GREEN], TSrc::G(pin), a);
TBlend::OpC(pout[TDest::BLUE], TSrc::B(pin), a);
TBlend::OpA(pout[TDest::ALPHA], a);
}
pout+=4;
pin+=step;
}
}
typedef void (*CopyFunc)(uint8_t *pout, const uint8_t *pin, int count, int step, uint8_t r, uint8_t g, uint8_t b);
static const CopyFunc copyfuncs[]=
{
iCopyColors<cRGB, cBGRA, bCopy>,
iCopyColors<cRGBT, cBGRA, bCopy>,
iCopyColors<cRGBA, cBGRA, bCopy>,
iCopyColors<cIA, cBGRA, bCopy>,
iCopyColors<cCMYK, cBGRA, bCopy>,
iCopyColors<cYCbCr, cBGRA, bCopy>,
iCopyColors<cBGR, cBGRA, bCopy>,
iCopyColors<cBGRA, cBGRA, bCopy>,
iCopyColors<cI16, cBGRA, bCopy>,
iCopyColors<cRGB555, cBGRA, bCopy>,
iCopyColors<cPalEntry, cBGRA, bCopy>
};
//===========================================================================
//
// True Color texture copy function
//
//===========================================================================
void FBitmap::CopyPixelDataRGB(int originx, int originy, const uint8_t *patch, int srcwidth,
int srcheight, int step_x, int step_y, int rotate, int ct,
int r, int g, int b)
{
uint8_t *buffer = data + 4 * originx + Pitch * originy;
for (int y=0;y<srcheight;y++)
{
copyfuncs[ct](&buffer[y*Pitch], &patch[y*step_y], srcwidth, step_x, r, g, b);
}
}
template<class TDest, class TBlend>
void iCopyPaletted(uint8_t *buffer, const uint8_t * patch, int srcwidth, int srcheight, int Pitch,
int step_x, int step_y, int rotate, PalEntry * palette)
{
int x,y,pos;
for (y=0;y<srcheight;y++)
{
pos = y*Pitch;
for (x=0;x<srcwidth;x++,pos+=4)
{
int v=(unsigned char)patch[y*step_y+x*step_x];
int a = palette[v].a;
if (TBlend::ProcessAlpha0() || a)
{
TBlend::OpC(buffer[pos + TDest::RED], palette[v].r, a);
TBlend::OpC(buffer[pos + TDest::GREEN], palette[v].g, a);
TBlend::OpC(buffer[pos + TDest::BLUE], palette[v].b, a);
TBlend::OpA(buffer[pos + TDest::ALPHA], a);
}
}
}
}
typedef void (*CopyPalettedFunc)(uint8_t *buffer, const uint8_t * patch, int srcwidth, int srcheight, int Pitch,
int step_x, int step_y, int rotate, PalEntry * palette);
//===========================================================================
//
// Paletted to True Color texture copy function
//
//===========================================================================
void FBitmap::CopyPixelData(int originx, int originy, const uint8_t * patch, int srcwidth, int srcheight,
int step_x, int step_y, int rotate, PalEntry * palette)
{
uint8_t *buffer = data + 4*originx + Pitch*originy;
iCopyPaletted<cBGRA, bCopy>(buffer, patch, srcwidth, srcheight, Pitch,
step_x, step_y, rotate, palette);
}

View file

@ -0,0 +1,360 @@
/*
** bitmap.h
**
**---------------------------------------------------------------------------
** Copyright 2008 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.
**---------------------------------------------------------------------------
**
**
*/
#ifndef __BITMAP_H__
#define __BITMAP_H__
#include <algorithm>
#include "palentry.h"
#include <string.h>
#include <stdlib.h>
#include "templates.h"
struct FCopyInfo;
struct FClipRect
{
int x, y, width, height;
bool Intersect(int ix, int iy, int iw, int ih);
};
typedef int blend_t;
enum
{
BLENDBITS = 16,
BLENDUNIT = (1<<BLENDBITS)
};
enum ColorType
{
CF_RGB,
CF_RGBT,
CF_RGBA,
CF_IA,
CF_CMYK,
CF_YCbCr,
CF_BGR,
CF_BGRA,
CF_I16,
CF_RGB555,
CF_PalEntry
};
class FBitmap
{
protected:
uint8_t *data;
int Width;
int Height;
int Pitch;
bool FreeBuffer;
public:
FBitmap()
{
data = NULL;
Width = Height = 0;
Pitch = 0;
FreeBuffer = false;
}
FBitmap(uint8_t *buffer, int pitch, int width, int height)
{
data = buffer;
Pitch = pitch;
Width = width;
Height = height;
FreeBuffer = false;
}
FBitmap(const FBitmap &other) = delete; // disallow because in nearly all cases this creates an unwanted copy.
FBitmap(FBitmap &&other)
{
data = other.data;
Pitch = other.Pitch;
Width = other.Width;
Height = other.Height;
FreeBuffer = other.FreeBuffer;
other.data = nullptr;
other.FreeBuffer = false;
}
FBitmap &operator=(const FBitmap &other) = delete; // disallow because in nearly all cases this creates an unwanted copy. Use Copy instead.
FBitmap &operator=(FBitmap &&other)
{
if (data != nullptr && FreeBuffer) delete[] data;
data = other.data;
Pitch = other.Pitch;
Width = other.Width;
Height = other.Height;
FreeBuffer = other.FreeBuffer;
other.data = nullptr;
other.FreeBuffer = false;
return *this;
}
void Copy(const FBitmap &other, bool deep = true)
{
if (data != nullptr && FreeBuffer) delete[] data;
Pitch = other.Pitch;
Width = other.Width;
Height = other.Height;
FreeBuffer = deep;
if (deep)
{
data = new uint8_t[Pitch * Height];
memcpy(data, other.data, Pitch * Height);
}
else
{
data = other.data;
}
}
~FBitmap()
{
Destroy();
}
void Destroy()
{
if (data != NULL && FreeBuffer) delete [] data;
data = NULL;
FreeBuffer = false;
}
bool Create (int w, int h)
{
Pitch = w*4;
Width = w;
Height = h;
data = new uint8_t[4*w*h];
memset(data, 0, 4*w*h);
FreeBuffer = true;
return data != NULL;
}
int GetHeight() const
{
return Height;
}
int GetWidth() const
{
return Width;
}
int GetPitch() const
{
return Pitch;
}
const uint8_t *GetPixels() const
{
return data;
}
uint8_t *GetPixels()
{
return data;
}
void CopyPixelDataRGB(int originx, int originy, const uint8_t *patch, int srcwidth,
int srcheight, int step_x, int step_y, int rotate, int ct,
/* for PNG tRNS */ int r=0, int g=0, int b=0);
void CopyPixelData(int originx, int originy, const uint8_t * patch, int srcwidth, int srcheight,
int step_x, int step_y, int rotate, PalEntry * palette);
void Blit(int originx, int originy, const FBitmap &src)
{
CopyPixelDataRGB(originx, originy, src.GetPixels(), src.GetWidth(), src.GetHeight(), 4, src.GetWidth()*4, 0, CF_BGRA);
}
};
//===========================================================================
//
// True color conversion classes for the different pixel formats
// used by the supported texture formats
//
//===========================================================================
struct cRGB
{
static __forceinline unsigned char R(const unsigned char * p) { return p[0]; }
static __forceinline unsigned char G(const unsigned char * p) { return p[1]; }
static __forceinline unsigned char B(const unsigned char * p) { return p[2]; }
static __forceinline unsigned char A(const unsigned char * p, uint8_t x, uint8_t y, uint8_t z) { return 255; }
static __forceinline int Gray(const unsigned char * p) { return (p[0]*77 + p[1]*143 + p[2]*36)>>8; }
};
struct cRGBT
{
static __forceinline unsigned char R(const unsigned char * p) { return p[0]; }
static __forceinline unsigned char G(const unsigned char * p) { return p[1]; }
static __forceinline unsigned char B(const unsigned char * p) { return p[2]; }
static __forceinline unsigned char A(const unsigned char * p, uint8_t r, uint8_t g, uint8_t b) { return (p[0] != r || p[1] != g || p[2] != b) ? 255 : 0; }
static __forceinline int Gray(const unsigned char * p) { return (p[0]*77 + p[1]*143 + p[2]*36)>>8; }
};
struct cRGBA
{
enum
{
RED = 0,
GREEN = 1,
BLUE = 1,
ALPHA = 3
};
static __forceinline unsigned char R(const unsigned char * p) { return p[0]; }
static __forceinline unsigned char G(const unsigned char * p) { return p[1]; }
static __forceinline unsigned char B(const unsigned char * p) { return p[2]; }
static __forceinline unsigned char A(const unsigned char * p, uint8_t x, uint8_t y, uint8_t z) { return p[3]; }
static __forceinline int Gray(const unsigned char * p) { return (p[0]*77 + p[1]*143 + p[2]*36)>>8; }
};
struct cIA
{
static __forceinline unsigned char R(const unsigned char * p) { return p[0]; }
static __forceinline unsigned char G(const unsigned char * p) { return p[0]; }
static __forceinline unsigned char B(const unsigned char * p) { return p[0]; }
static __forceinline unsigned char A(const unsigned char * p, uint8_t x, uint8_t y, uint8_t z) { return p[1]; }
static __forceinline int Gray(const unsigned char * p) { return p[0]; }
};
struct cCMYK
{
static __forceinline unsigned char R(const unsigned char * p) { return p[3] - (((256-p[0])*p[3]) >> 8); }
static __forceinline unsigned char G(const unsigned char * p) { return p[3] - (((256-p[1])*p[3]) >> 8); }
static __forceinline unsigned char B(const unsigned char * p) { return p[3] - (((256-p[2])*p[3]) >> 8); }
static __forceinline unsigned char A(const unsigned char * p, uint8_t x, uint8_t y, uint8_t z) { return 255; }
static __forceinline int Gray(const unsigned char * p) { return (R(p)*77 + G(p)*143 + B(p)*36)>>8; }
};
struct cYCbCr
{
static __forceinline unsigned char R(const unsigned char * p) { return clamp((int)(p[0] + 1.40200 * (int(p[2]) - 0x80)), 0, 255); }
static __forceinline unsigned char G(const unsigned char * p) { return clamp((int)(p[0] - 0.34414 * (int(p[1] - 0x80)) - 0.71414 * (int(p[2]) - 0x80)), 0, 255); }
static __forceinline unsigned char B(const unsigned char * p) { return clamp((int)(p[0] + 1.77200 * (int(p[1]) - 0x80)), 0, 255); }
static __forceinline unsigned char A(const unsigned char * p, uint8_t x, uint8_t y, uint8_t z) { return 255; }
static __forceinline int Gray(const unsigned char * p) { return (R(p) * 77 + G(p) * 143 + B(p) * 36) >> 8; }
};
struct cBGR
{
static __forceinline unsigned char R(const unsigned char * p) { return p[2]; }
static __forceinline unsigned char G(const unsigned char * p) { return p[1]; }
static __forceinline unsigned char B(const unsigned char * p) { return p[0]; }
static __forceinline unsigned char A(const unsigned char * p, uint8_t x, uint8_t y, uint8_t z) { return 255; }
static __forceinline int Gray(const unsigned char * p) { return (p[2]*77 + p[1]*143 + p[0]*36)>>8; }
};
struct cBGRA
{
enum
{
RED = 2,
GREEN = 1,
BLUE = 0,
ALPHA = 3
};
static __forceinline unsigned char R(const unsigned char * p) { return p[2]; }
static __forceinline unsigned char G(const unsigned char * p) { return p[1]; }
static __forceinline unsigned char B(const unsigned char * p) { return p[0]; }
static __forceinline unsigned char A(const unsigned char * p, uint8_t x, uint8_t y, uint8_t z) { return p[3]; }
static __forceinline int Gray(const unsigned char * p) { return (p[2]*77 + p[1]*143 + p[0]*36)>>8; }
};
struct cARGB
{
enum
{
RED = 1,
GREEN = 2,
BLUE = 3,
ALPHA = 0
};
static __forceinline unsigned char R(const unsigned char * p) { return p[1]; }
static __forceinline unsigned char G(const unsigned char * p) { return p[2]; }
static __forceinline unsigned char B(const unsigned char * p) { return p[3]; }
static __forceinline unsigned char A(const unsigned char * p, uint8_t x, uint8_t y, uint8_t z) { return p[0]; }
static __forceinline int Gray(const unsigned char * p) { return (p[1]*77 + p[2]*143 + p[3]*36)>>8; }
};
struct cI16
{
static __forceinline unsigned char R(const unsigned char * p) { return p[1]; }
static __forceinline unsigned char G(const unsigned char * p) { return p[1]; }
static __forceinline unsigned char B(const unsigned char * p) { return p[1]; }
static __forceinline unsigned char A(const unsigned char * p, uint8_t x, uint8_t y, uint8_t z) { return 255; }
static __forceinline int Gray(const unsigned char * p) { return p[1]; }
};
struct cRGB555
{
static __forceinline unsigned char R(const unsigned char * p) { return (((*(uint16_t*)p)&0x1f)<<3); }
static __forceinline unsigned char G(const unsigned char * p) { return (((*(uint16_t*)p)&0x3e0)>>2); }
static __forceinline unsigned char B(const unsigned char * p) { return (((*(uint16_t*)p)&0x7c00)>>7); }
static __forceinline unsigned char A(const unsigned char * p, uint8_t x, uint8_t y, uint8_t z) { return 255; }
static __forceinline int Gray(const unsigned char * p) { return (R(p)*77 + G(p)*143 + B(p)*36)>>8; }
};
struct cPalEntry
{
static __forceinline unsigned char R(const unsigned char * p) { return ((PalEntry*)p)->r; }
static __forceinline unsigned char G(const unsigned char * p) { return ((PalEntry*)p)->g; }
static __forceinline unsigned char B(const unsigned char * p) { return ((PalEntry*)p)->b; }
static __forceinline unsigned char A(const unsigned char * p, uint8_t x, uint8_t y, uint8_t z) { return ((PalEntry*)p)->a; }
static __forceinline int Gray(const unsigned char * p) { return (R(p)*77 + G(p)*143 + B(p)*36)>>8; }
};
struct bCopy
{
static __forceinline void OpC(uint8_t &d, uint8_t s, uint8_t a) { d = s; }
static __forceinline void OpA(uint8_t &d, uint8_t s) { d = s; }
static __forceinline bool ProcessAlpha0() { return false; }
};
#endif

View file

@ -0,0 +1,248 @@
/*
** buildtexture.cpp
** Handling Build textures (now as a usable editing feature!)
**
**---------------------------------------------------------------------------
** Copyright 2004-2006 Randy Heit
** Copyright 2018 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 <stdint.h>
#include "zstring.h"
#include "files.h"
#include "templates.h"
#include "bitmap.h"
#include "textures/textures.h"
#include "image.h"
//==========================================================================
//
// A texture defined in a Build TILESxxx.ART file
//
//==========================================================================
class FBuildTexture : public FImageSource
{
public:
FBuildTexture (const FString &pathprefix, int tilenum, const uint8_t *pixels, int width, int height, int left, int top);
TArray<uint8_t> CreatePalettedPixels(int conversion) override;
protected:
const uint8_t *RawPixels;
};
//==========================================================================
//
//
//
//==========================================================================
FBuildTexture::FBuildTexture(const FString &pathprefix, int tilenum, const uint8_t *pixels, int width, int height, int left, int top)
: RawPixels (pixels)
{
Width = width;
Height = height;
LeftOffset = left;
TopOffset = top;
}
TArray<uint8_t> FBuildTexture::CreatePalettedPixels(int conversion)
{
TArray<uint8_t> Pixels(Width * Height, true);
memcpy(Pixels.Data(), RawPixels, Width * Height);
return Pixels;
}
#if 0
//===========================================================================
//
// AddTiles
//
// Adds all the tiles in an artfile to the texture manager.
//
//===========================================================================
void FTextureManager::AddTiles (const FString &pathprefix, const void *tiles)
{
// int numtiles = LittleLong(((uint32_t *)tiles)[1]); // This value is not reliable
int tilestart = LittleLong(((uint32_t *)tiles)[2]);
int tileend = LittleLong(((uint32_t *)tiles)[3]);
const uint16_t *tilesizx = &((const uint16_t *)tiles)[8];
const uint16_t *tilesizy = &tilesizx[tileend - tilestart + 1];
const uint32_t *picanm = (const uint32_t *)&tilesizy[tileend - tilestart + 1];
const uint8_t *tiledata = (const uint8_t *)&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]);
uint32_t anm = LittleLong(picanm[pic]);
int xoffs = (int8_t)((anm >> 8) & 255) + width/2;
int yoffs = (int8_t)((anm >> 16) & 255) + height/2;
int size = width*height;
//FTextureID texnum;
FTexture *tex;
if (width <= 0 || height <= 0) continue;
tex = new FImageTexture(new FBuildTexture (pathprefix, i, tiledata, width, height, xoffs, yoffs));
//texnum = AddTexture (tex);
tiledata += size;
tex->Name.Format("%sTILE%04d", pathprefix.GetChars(), i);
// I have no idea if this makes sense or if we better leave animation control to the game code.
// To be decided later.
#if 0
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.
AddSimpleAnim (texnum, picanm[pic] & 63, type, speed);
}
#endif
// 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
#if 0
int rotType = (anm >> 28) & 7;
if (rotType == 1)
{
spriteframe_t rot;
rot.Texture[0] =
rot.Texture[1] = texnum;
for (int j = 1; j < 4; ++j)
{
rot.Texture[j*2] =
rot.Texture[j*2+1] =
rot.Texture[16-j*2] =
rot.Texture[17-j*2] = texnum.GetIndex() + j;
}
rot.Texture[8] =
rot.Texture[9] = texnum.GetIndex() + 4;
rot.Flip = 0x00FC;
rot.Voxel = NULL;
tex->Rotations = SpriteFrames.Push (rot);
}
else if (rotType == 2)
{
spriteframe_t rot;
rot.Texture[0] =
rot.Texture[1] = texnum;
for (int j = 1; j < 8; ++j)
{
rot.Texture[16-j*2] =
rot.Texture[17-j*2] = texnum.GetIndex() + j;
}
rot.Flip = 0;
rot.Voxel = NULL;
tex->Rotations = SpriteFrames.Push (rot);
}
#endif
}
}
//===========================================================================
//
// CountTiles
//
// Returns the number of tiles provided by an artfile
//
//===========================================================================
static int CountTiles (const void *tiles)
{
int version = LittleLong(*(uint32_t *)tiles);
if (version != 1)
{
return 0;
}
int tilestart = LittleLong(((uint32_t *)tiles)[2]);
int tileend = LittleLong(((uint32_t *)tiles)[3]);
return tileend >= tilestart ? tileend - tilestart + 1 : 0;
}
//===========================================================================
//
// R_CountBuildTiles
//
// Returns the number of tiles found. Also loads all the data for
// R_InitBuildTiles() to process later.
//
//===========================================================================
void FTextureManager::InitBuildTiles(TArray<FString> &tileFiles)
{
int numtiles;
int totaltiles = 0;
for (auto &artpath : tileFiles)
{
int lumpnum = fileSystem.FindFile(artpath);
if (lumpnum >= 0)
{
BuildTileData.Reserve(1);
auto &artdata = BuildTileData.Last();
artdata = fileSystem.GetFileData(lumpnum);
if ((numtiles = CountTiles(artdata.Data())) > 0)
{
AddTiles("", &artdata[0]);
totaltiles += numtiles;
}
}
}
}
#endif

View file

@ -0,0 +1,760 @@
/*
** pngtexture.cpp
** Texture class for DDS images
**
**---------------------------------------------------------------------------
** Copyright 2006-2007 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.
**---------------------------------------------------------------------------
**
** DDS is short for "DirectDraw Surface" and is essentially that. It's
** interesting to us because it is a standard file format for DXTC/S3TC
** encoded images. Look up "DDS File Reference" in the DirectX SDK or
** the online MSDN documentation to the specs for this file format. Look up
** "Compressed Texture Resources" for information about DXTC encoding.
**
** Perhaps the most important part of DXTC to realize is that every 4x4
** pixel block can only have four different colors, and only two of those
** are discrete. So depending on the texture, there may be very noticable
** quality degradation, or it may look virtually indistinguishable from
** the uncompressed texture.
**
** Note: Although this class supports reading RGB textures from a DDS,
** DO NOT use DDS images with plain RGB data. PNG does everything useful
** better. Since DDS lets the R, G, B, and A components lie anywhere in
** the pixel data, it is fairly inefficient to process.
*/
#include <stdint.h>
#include "files.h"
#include "bitmap.h"
#include "image.h"
#include "m_png.h"
#include "cache1d.h"
// Since we want this to compile under Linux too, we need to define this
// stuff ourselves instead of including a DirectX header.
enum
{
ID_DDS = MAKE_ID('D', 'D', 'S', ' '),
ID_DXT1 = MAKE_ID('D', 'X', 'T', '1'),
ID_DXT2 = MAKE_ID('D', 'X', 'T', '2'),
ID_DXT3 = MAKE_ID('D', 'X', 'T', '3'),
ID_DXT4 = MAKE_ID('D', 'X', 'T', '4'),
ID_DXT5 = MAKE_ID('D', 'X', 'T', '5'),
// Bits in dwFlags
DDSD_CAPS = 0x00000001,
DDSD_HEIGHT = 0x00000002,
DDSD_WIDTH = 0x00000004,
DDSD_PITCH = 0x00000008,
DDSD_PIXELFORMAT = 0x00001000,
DDSD_MIPMAPCOUNT = 0x00020000,
DDSD_LINEARSIZE = 0x00080000,
DDSD_DEPTH = 0x00800000,
// Bits in ddpfPixelFormat
DDPF_ALPHAPIXELS = 0x00000001,
DDPF_FOURCC = 0x00000004,
DDPF_RGB = 0x00000040,
// Bits in DDSCAPS2.dwCaps1
DDSCAPS_COMPLEX = 0x00000008,
DDSCAPS_TEXTURE = 0x00001000,
DDSCAPS_MIPMAP = 0x00400000,
// Bits in DDSCAPS2.dwCaps2
DDSCAPS2_CUBEMAP = 0x00000200,
DDSCAPS2_CUBEMAP_POSITIVEX = 0x00000400,
DDSCAPS2_CUBEMAP_NEGATIVEX = 0x00000800,
DDSCAPS2_CUBEMAP_POSITIVEY = 0x00001000,
DDSCAPS2_CUBEMAP_NEGATIVEY = 0x00002000,
DDSCAPS2_CUBEMAP_POSITIVEZ = 0x00004000,
DDSCAPS2_CUBEMAP_NEGATIZEZ = 0x00008000,
DDSCAPS2_VOLUME = 0x00200000,
};
//==========================================================================
//
//
//
//==========================================================================
struct DDPIXELFORMAT
{
uint32_t Size; // Must be 32
uint32_t Flags;
uint32_t FourCC;
uint32_t RGBBitCount;
uint32_t RBitMask, GBitMask, BBitMask;
uint32_t RGBAlphaBitMask;
};
struct DDCAPS2
{
uint32_t Caps1, Caps2;
uint32_t Reserved[2];
};
struct DDSURFACEDESC2
{
uint32_t Size; // Must be 124. DevIL claims some writers set it to 'DDS ' instead.
uint32_t Flags;
uint32_t Height;
uint32_t Width;
union
{
int32_t Pitch;
uint32_t LinearSize;
};
uint32_t Depth;
uint32_t MipMapCount;
uint32_t Reserved1[11];
DDPIXELFORMAT PixelFormat;
DDCAPS2 Caps;
uint32_t Reserved2;
};
struct DDSFileHeader
{
uint32_t Magic;
DDSURFACEDESC2 Desc;
};
//==========================================================================
//
// A DDS image, with DXTx compression
//
//==========================================================================
class FDDSTexture : public FImageSource
{
enum
{
PIX_Palette = 0,
PIX_Alphatex = 1,
PIX_ARGB = 2
};
public:
FDDSTexture (FileReader &lump, void *surfdesc);
protected:
uint32_t Format;
uint32_t RMask, GMask, BMask, AMask;
uint8_t RShiftL, GShiftL, BShiftL, AShiftL;
uint8_t RShiftR, GShiftR, BShiftR, AShiftR;
int32_t Pitch;
uint32_t LinearSize;
static void CalcBitShift (uint32_t mask, uint8_t *lshift, uint8_t *rshift);
void ReadRGB (FileReader &lump, uint8_t *buffer, int pixelmode);
void DecompressDXT1 (FileReader &lump, uint8_t *buffer, int pixelmode);
void DecompressDXT3 (FileReader &lump, bool premultiplied, uint8_t *buffer, int pixelmode);
void DecompressDXT5 (FileReader &lump, bool premultiplied, uint8_t *buffer, int pixelmode);
int CopyPixels(FBitmap *bmp, int conversion) override;
friend class FTexture;
};
//==========================================================================
//
//
//
//==========================================================================
static bool CheckDDS (FileReader &file)
{
DDSFileHeader Header;
file.Seek(0, FileReader::SeekSet);
if (file.Read (&Header, sizeof(Header)) != sizeof(Header))
{
return false;
}
return Header.Magic == ID_DDS &&
(LittleLong(Header.Desc.Size) == sizeof(DDSURFACEDESC2) || Header.Desc.Size == ID_DDS) &&
LittleLong(Header.Desc.PixelFormat.Size) == sizeof(DDPIXELFORMAT) &&
(LittleLong(Header.Desc.Flags) & (DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT)) == (DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT) &&
Header.Desc.Width != 0 &&
Header.Desc.Height != 0;
}
//==========================================================================
//
//
//
//==========================================================================
FImageSource *DDSImage_TryCreate (FileReader &data)
{
union
{
DDSURFACEDESC2 surfdesc;
uint32_t byteswapping[sizeof(DDSURFACEDESC2) / 4];
};
if (!CheckDDS(data)) return NULL;
data.Seek(4, FileReader::SeekSet);
data.Read (&surfdesc, sizeof(surfdesc));
#ifdef __BIG_ENDIAN__
// Every single element of the header is a uint32_t
for (unsigned int i = 0; i < sizeof(DDSURFACEDESC2) / 4; ++i)
{
byteswapping[i] = LittleLong(byteswapping[i]);
}
// Undo the byte swap for the pixel format
surfdesc.PixelFormat.FourCC = LittleLong(surfdesc.PixelFormat.FourCC);
#endif
if (surfdesc.PixelFormat.Flags & DDPF_FOURCC)
{
// Check for supported FourCC
if (surfdesc.PixelFormat.FourCC != ID_DXT1 &&
surfdesc.PixelFormat.FourCC != ID_DXT2 &&
surfdesc.PixelFormat.FourCC != ID_DXT3 &&
surfdesc.PixelFormat.FourCC != ID_DXT4 &&
surfdesc.PixelFormat.FourCC != ID_DXT5)
{
return NULL;
}
if (!(surfdesc.Flags & DDSD_LINEARSIZE))
{
return NULL;
}
}
else if (surfdesc.PixelFormat.Flags & DDPF_RGB)
{
if ((surfdesc.PixelFormat.RGBBitCount >> 3) < 1 ||
(surfdesc.PixelFormat.RGBBitCount >> 3) > 4)
{
return NULL;
}
if ((surfdesc.Flags & DDSD_PITCH) && (surfdesc.Pitch <= 0))
{
return NULL;
}
}
else
{
return NULL;
}
return new FDDSTexture (data, &surfdesc);
}
//==========================================================================
//
//
//
//==========================================================================
FDDSTexture::FDDSTexture (FileReader &lump, void *vsurfdesc)
{
DDSURFACEDESC2 *surf = (DDSURFACEDESC2 *)vsurfdesc;
bMasked = false;
Width = uint16_t(surf->Width);
Height = uint16_t(surf->Height);
if (surf->PixelFormat.Flags & DDPF_FOURCC)
{
Format = surf->PixelFormat.FourCC;
Pitch = 0;
LinearSize = surf->LinearSize;
}
else // DDPF_RGB
{
Format = surf->PixelFormat.RGBBitCount >> 3;
CalcBitShift (RMask = surf->PixelFormat.RBitMask, &RShiftL, &RShiftR);
CalcBitShift (GMask = surf->PixelFormat.GBitMask, &GShiftL, &GShiftR);
CalcBitShift (BMask = surf->PixelFormat.BBitMask, &BShiftL, &BShiftR);
if (surf->PixelFormat.Flags & DDPF_ALPHAPIXELS)
{
CalcBitShift (AMask = surf->PixelFormat.RGBAlphaBitMask, &AShiftL, &AShiftR);
}
else
{
AMask = 0;
AShiftL = AShiftR = 0;
}
if (surf->Flags & DDSD_PITCH)
{
Pitch = surf->Pitch;
}
else
{
Pitch = (Width * Format + 3) & ~3;
}
LinearSize = Pitch * Height;
}
}
//==========================================================================
//
// Returns the number of bits the color must be shifted to produce
// an 8-bit value, as in:
//
// c = (color & mask) << lshift;
// c |= c >> rshift;
// c >>= 24;
//
// For any color of at least 4 bits, this ensures that the result
// of the calculation for c will be fully saturated, given a maximum
// value for the input bit mask.
//
//==========================================================================
void FDDSTexture::CalcBitShift (uint32_t mask, uint8_t *lshiftp, uint8_t *rshiftp)
{
uint8_t shift;
if (mask == 0)
{
*lshiftp = *rshiftp = 0;
return;
}
shift = 0;
while ((mask & 0x80000000) == 0)
{
mask <<= 1;
shift++;
}
*lshiftp = shift;
shift = 0;
while (mask & 0x80000000)
{
mask <<= 1;
shift++;
}
*rshiftp = shift;
}
//==========================================================================
//
// Note that pixel size == 8 is column-major, but 32 is row-major!
//
//==========================================================================
void FDDSTexture::ReadRGB (FileReader &lump, uint8_t *buffer, int pixelmode)
{
uint32_t x, y;
uint32_t amask = AMask == 0 ? 0 : 0x80000000 >> AShiftL;
uint8_t *linebuff = new uint8_t[Pitch];
for (y = Height; y > 0; --y)
{
uint8_t *buffp = linebuff;
uint8_t *pixelp = pixelmode == PIX_ARGB ? buffer + 4 * (y - 1)*Width : buffer + y - 1;
lump.Read (linebuff, Pitch);
for (x = Width; x > 0; --x)
{
uint32_t c;
if (Format == 4)
{
c = LittleLong(*(uint32_t *)buffp); buffp += 4;
}
else if (Format == 2)
{
c = LittleShort(*(uint16_t *)buffp); buffp += 2;
}
else if (Format == 3)
{
c = buffp[0] | (buffp[1] << 8) | (buffp[2] << 16); buffp += 3;
}
else // Format == 1
{
c = *buffp++;
}
if (pixelmode != PIX_ARGB)
{
assert(false);
}
else
{
uint32_t r = (c & RMask) << RShiftL; r |= r >> RShiftR;
uint32_t g = (c & GMask) << GShiftL; g |= g >> GShiftR;
uint32_t b = (c & BMask) << BShiftL; b |= b >> BShiftR;
uint32_t a = (c & AMask) << AShiftL; a |= a >> AShiftR;
pixelp[0] = (uint8_t)(b>>24);
pixelp[1] = (uint8_t)(g>>24);
pixelp[2] = (uint8_t)(r>>24);
pixelp[3] = (uint8_t)(a>>24);
pixelp+=4;
}
}
}
delete[] linebuff;
}
//==========================================================================
//
//
//
//==========================================================================
void FDDSTexture::DecompressDXT1 (FileReader &lump, uint8_t *buffer, int pixelmode)
{
const long blocklinelen = ((Width + 3) >> 2) << 3;
uint8_t *blockbuff = new uint8_t[blocklinelen];
uint8_t *block;
PalEntry color[4];
uint8_t palcol[4] = { 0,0,0,0 }; // shut up compiler warnings.
int ox, oy, x, y, i;
color[0].a = 255;
color[1].a = 255;
color[2].a = 255;
for (oy = 0; oy < Height; oy += 4)
{
lump.Read (blockbuff, blocklinelen);
block = blockbuff;
for (ox = 0; ox < Width; ox += 4)
{
uint16_t color16[2] = { LittleShort(((uint16_t *)block)[0]), LittleShort(((uint16_t *)block)[1]) };
// Convert color from R5G6B5 to R8G8B8.
for (i = 1; i >= 0; --i)
{
color[i].r = ((color16[i] & 0xF800) >> 8) | (color16[i] >> 13);
color[i].g = ((color16[i] & 0x07E0) >> 3) | ((color16[i] & 0x0600) >> 9);
color[i].b = ((color16[i] & 0x001F) << 3) | ((color16[i] & 0x001C) >> 2);
}
if (color16[0] > color16[1])
{ // Four-color block: derive the other two colors.
color[2].r = (color[0].r + color[0].r + color[1].r + 1) / 3;
color[2].g = (color[0].g + color[0].g + color[1].g + 1) / 3;
color[2].b = (color[0].b + color[0].b + color[1].b + 1) / 3;
color[3].r = (color[0].r + color[1].r + color[1].r + 1) / 3;
color[3].g = (color[0].g + color[1].g + color[1].g + 1) / 3;
color[3].b = (color[0].b + color[1].b + color[1].b + 1) / 3;
color[3].a = 255;
}
else
{ // Three-color block: derive the other color.
color[2].r = (color[0].r + color[1].r) / 2;
color[2].g = (color[0].g + color[1].g) / 2;
color[2].b = (color[0].b + color[1].b) / 2;
color[3].a = color[3].b = color[3].g = color[3].r = 0;
// If you have a three-color block, presumably that transparent
// color is going to be used.
bMasked = true;
}
// Pick colors from the palette for each of the four colors.
if (pixelmode != PIX_ARGB) for (i = 3; i >= 0; --i)
{
assert(false);
}
// Now decode this 4x4 block to the pixel buffer.
for (y = 0; y < 4; ++y)
{
if (oy + y >= Height)
{
break;
}
uint8_t yslice = block[4 + y];
for (x = 0; x < 4; ++x)
{
if (ox + x >= Width)
{
break;
}
int ci = (yslice >> (x + x)) & 3;
if (pixelmode != PIX_ARGB)
{
buffer[oy + y + (ox + x) * Height] = palcol[ci];
}
else
{
uint8_t * tcp = &buffer[(ox + x)*4 + (oy + y) * Width*4];
tcp[0] = color[ci].b;
tcp[1] = color[ci].g;
tcp[2] = color[ci].r;
tcp[3] = color[ci].a;
}
}
}
block += 8;
}
}
delete[] blockbuff;
}
//==========================================================================
//
// DXT3: Decompression is identical to DXT1, except every 64-bit block is
// preceded by another 64-bit block with explicit alpha values.
//
//==========================================================================
void FDDSTexture::DecompressDXT3 (FileReader &lump, bool premultiplied, uint8_t *buffer, int pixelmode)
{
const long blocklinelen = ((Width + 3) >> 2) << 4;
uint8_t *blockbuff = new uint8_t[blocklinelen];
uint8_t *block;
PalEntry color[4];
uint8_t palcol[4] = { 0,0,0,0 };
int ox, oy, x, y, i;
for (oy = 0; oy < Height; oy += 4)
{
lump.Read (blockbuff, blocklinelen);
block = blockbuff;
for (ox = 0; ox < Width; ox += 4)
{
uint16_t color16[2] = { LittleShort(((uint16_t *)block)[4]), LittleShort(((uint16_t *)block)[5]) };
// Convert color from R5G6B5 to R8G8B8.
for (i = 1; i >= 0; --i)
{
color[i].r = ((color16[i] & 0xF800) >> 8) | (color16[i] >> 13);
color[i].g = ((color16[i] & 0x07E0) >> 3) | ((color16[i] & 0x0600) >> 9);
color[i].b = ((color16[i] & 0x001F) << 3) | ((color16[i] & 0x001C) >> 2);
}
// Derive the other two colors.
color[2].r = (color[0].r + color[0].r + color[1].r + 1) / 3;
color[2].g = (color[0].g + color[0].g + color[1].g + 1) / 3;
color[2].b = (color[0].b + color[0].b + color[1].b + 1) / 3;
color[3].r = (color[0].r + color[1].r + color[1].r + 1) / 3;
color[3].g = (color[0].g + color[1].g + color[1].g + 1) / 3;
color[3].b = (color[0].b + color[1].b + color[1].b + 1) / 3;
// Pick colors from the palette for each of the four colors.
if (pixelmode != PIX_ARGB) for (i = 3; i >= 0; --i)
{
assert(false);
}
// Now decode this 4x4 block to the pixel buffer.
for (y = 0; y < 4; ++y)
{
if (oy + y >= Height)
{
break;
}
uint8_t yslice = block[12 + y];
uint16_t yalphaslice = LittleShort(((uint16_t *)block)[y]);
for (x = 0; x < 4; ++x)
{
if (ox + x >= Width)
{
break;
}
if (pixelmode == PIX_Palette)
{
buffer[oy + y + (ox + x) * Height] = ((yalphaslice >> (x*4)) & 15) < 8 ?
(bMasked = true, 0) : palcol[(yslice >> (x + x)) & 3];
}
else if (pixelmode == PIX_Alphatex)
{
int alphaval = ((yalphaslice >> (x * 4)) & 15);
int palval = palcol[(yslice >> (x + x)) & 3];
buffer[oy + y + (ox + x) * Height] = palval * alphaval / 15;
}
else
{
uint8_t * tcp = &buffer[(ox + x)*4 + (oy + y) * Width*4];
int c = (yslice >> (x + x)) & 3;
tcp[0] = color[c].b;
tcp[1] = color[c].g;
tcp[2] = color[c].r;
tcp[3] = ((yalphaslice >> (x * 4)) & 15) * 0x11;
}
}
}
block += 16;
}
}
delete[] blockbuff;
}
//==========================================================================
//
// DXT5: Decompression is identical to DXT3, except every 64-bit alpha block
// contains interpolated alpha values, similar to the 64-bit color block.
//
//==========================================================================
void FDDSTexture::DecompressDXT5 (FileReader &lump, bool premultiplied, uint8_t *buffer, int pixelmode)
{
const long blocklinelen = ((Width + 3) >> 2) << 4;
uint8_t *blockbuff = new uint8_t[blocklinelen];
uint8_t *block;
PalEntry color[4];
uint8_t palcol[4] = { 0,0,0,0 };
uint32_t yalphaslice = 0;
int ox, oy, x, y, i;
for (oy = 0; oy < Height; oy += 4)
{
lump.Read (blockbuff, blocklinelen);
block = blockbuff;
for (ox = 0; ox < Width; ox += 4)
{
uint16_t color16[2] = { LittleShort(((uint16_t *)block)[4]), LittleShort(((uint16_t *)block)[5]) };
uint8_t alpha[8];
// Calculate the eight alpha values.
alpha[0] = block[0];
alpha[1] = block[1];
if (alpha[0] >= alpha[1])
{ // Eight-alpha block: derive the other six alphas.
for (i = 0; i < 6; ++i)
{
alpha[i + 2] = ((6 - i) * alpha[0] + (i + 1) * alpha[1] + 3) / 7;
}
}
else
{ // Six-alpha block: derive the other four alphas.
for (i = 0; i < 4; ++i)
{
alpha[i + 2] = ((4 - i) * alpha[0] + (i + 1) * alpha[1] + 2) / 5;
}
alpha[6] = 0;
alpha[7] = 255;
}
// Convert color from R5G6B5 to R8G8B8.
for (i = 1; i >= 0; --i)
{
color[i].r = ((color16[i] & 0xF800) >> 8) | (color16[i] >> 13);
color[i].g = ((color16[i] & 0x07E0) >> 3) | ((color16[i] & 0x0600) >> 9);
color[i].b = ((color16[i] & 0x001F) << 3) | ((color16[i] & 0x001C) >> 2);
}
// Derive the other two colors.
color[2].r = (color[0].r + color[0].r + color[1].r + 1) / 3;
color[2].g = (color[0].g + color[0].g + color[1].g + 1) / 3;
color[2].b = (color[0].b + color[0].b + color[1].b + 1) / 3;
color[3].r = (color[0].r + color[1].r + color[1].r + 1) / 3;
color[3].g = (color[0].g + color[1].g + color[1].g + 1) / 3;
color[3].b = (color[0].b + color[1].b + color[1].b + 1) / 3;
// Pick colors from the palette for each of the four colors.
if (pixelmode != PIX_ARGB) for (i = 3; i >= 0; --i)
{
assert(false);
}
// Now decode this 4x4 block to the pixel buffer.
for (y = 0; y < 4; ++y)
{
if (oy + y >= Height)
{
break;
}
// Alpha values are stored in 3 bytes for 2 rows
if ((y & 1) == 0)
{
yalphaslice = block[y*3] | (block[y*3+1] << 8) | (block[y*3+2] << 16);
}
else
{
yalphaslice >>= 12;
}
uint8_t yslice = block[12 + y];
for (x = 0; x < 4; ++x)
{
if (ox + x >= Width)
{
break;
}
if (pixelmode == PIX_Palette)
{
buffer[oy + y + (ox + x) * Height] = alpha[((yalphaslice >> (x*3)) & 7)] < 128 ?
(bMasked = true, 0) : palcol[(yslice >> (x + x)) & 3];
}
else if (pixelmode == PIX_Alphatex)
{
int alphaval = alpha[((yalphaslice >> (x * 3)) & 7)];
int palval = palcol[(yslice >> (x + x)) & 3];
buffer[oy + y + (ox + x) * Height] = palval * alphaval / 255;
}
else
{
uint8_t * tcp = &buffer[(ox + x)*4 + (oy + y) * Width*4];
int c = (yslice >> (x + x)) & 3;
tcp[0] = color[c].b;
tcp[1] = color[c].g;
tcp[2] = color[c].r;
tcp[3] = alpha[((yalphaslice >> (x*3)) & 7)];
}
}
}
block += 16;
}
}
delete[] blockbuff;
}
//===========================================================================
//
// FDDSTexture::CopyPixels
//
//===========================================================================
int FDDSTexture::CopyPixels(FBitmap *bmp, int conversion)
{
auto lump = kopenFileReader(Name, 0);
if (!lump.isOpen()) return -1; // Just leave the texture blank.
uint8_t *TexBuffer = bmp->GetPixels();
lump.Seek (sizeof(DDSURFACEDESC2) + 4, FileReader::SeekSet);
if (Format >= 1 && Format <= 4) // RGB: Format is # of bytes per pixel
{
ReadRGB (lump, TexBuffer, PIX_ARGB);
}
else if (Format == ID_DXT1)
{
DecompressDXT1 (lump, TexBuffer, PIX_ARGB);
}
else if (Format == ID_DXT3 || Format == ID_DXT2)
{
DecompressDXT3 (lump, Format == ID_DXT2, TexBuffer, PIX_ARGB);
}
else if (Format == ID_DXT5 || Format == ID_DXT4)
{
DecompressDXT5 (lump, Format == ID_DXT4, TexBuffer, PIX_ARGB);
}
return -1;
}

View file

@ -0,0 +1,339 @@
/*
** jpegtexture.cpp
** Texture class for JPEG images
**
**---------------------------------------------------------------------------
** Copyright 2006-2007 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 <stdio.h>
#include "files.h"
#include "printf.h"
#include "bitmap.h"
#include "image.h"
#include "cache1d.h"
extern "C"
{
#include <jpeglib.h>
}
struct FLumpSourceMgr : public jpeg_source_mgr
{
FileReader *Lump;
JOCTET Buffer[4096];
bool StartOfFile;
FLumpSourceMgr (FileReader *lump, j_decompress_ptr cinfo);
static void InitSource (j_decompress_ptr cinfo);
static boolean FillInputBuffer (j_decompress_ptr cinfo);
static void SkipInputData (j_decompress_ptr cinfo, long num_bytes);
static void TermSource (j_decompress_ptr cinfo);
};
//==========================================================================
//
//
//
//==========================================================================
void FLumpSourceMgr::InitSource (j_decompress_ptr cinfo)
{
((FLumpSourceMgr *)(cinfo->src))->StartOfFile = true;
}
//==========================================================================
//
//
//
//==========================================================================
boolean FLumpSourceMgr::FillInputBuffer (j_decompress_ptr cinfo)
{
FLumpSourceMgr *me = (FLumpSourceMgr *)(cinfo->src);
auto 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, FileReader::SeekCur);
FillInputBuffer (cinfo);
}
}
//==========================================================================
//
//
//
//==========================================================================
void FLumpSourceMgr::TermSource (j_decompress_ptr cinfo)
{
}
//==========================================================================
//
//
//
//==========================================================================
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 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);
}
//==========================================================================
//
// A JPEG texture
//
//==========================================================================
class FJPEGTexture : public FImageSource
{
public:
FJPEGTexture (int width, int height);
int CopyPixels(FBitmap *bmp, int conversion) override;
};
//==========================================================================
//
//
//
//==========================================================================
FImageSource *JPEGImage_TryCreate(FileReader & data)
{
union
{
uint32_t dw;
uint16_t w[2];
uint8_t b[4];
} first4bytes;
data.Seek(0, FileReader::SeekSet);
if (data.Read(&first4bytes, 4) < 4) return NULL;
if (first4bytes.b[0] != 0xFF || first4bytes.b[1] != 0xD8 || first4bytes.b[2] != 0xFF)
return NULL;
// 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, FileReader::SeekCur);
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 (BigShort(first4bytes.w[1]), BigShort(first4bytes.w[0]));
}
//==========================================================================
//
//
//
//==========================================================================
FJPEGTexture::FJPEGTexture (int width, int height)
{
bMasked = false;
Width = width;
Height = height;
}
//===========================================================================
//
// FJPEGTexture::CopyPixels
//
// Preserves the full color information (unlike software mode)
//
//===========================================================================
int FJPEGTexture::CopyPixels(FBitmap *bmp, int conversion)
{
PalEntry pe[256];
auto lump = kopenFileReader(Name, 0);
if (!lump.isOpen()) return -1; // Just leave the texture blank.
jpeg_decompress_struct cinfo;
jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
cinfo.err->output_message = JPEG_OutputMessage;
cinfo.err->error_exit = JPEG_ErrorExit;
jpeg_create_decompress(&cinfo);
FLumpSourceMgr sourcemgr(&lump, &cinfo);
try
{
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_YCbCr && cinfo.num_components == 3) ||
(cinfo.out_color_space == JCS_GRAYSCALE && cinfo.num_components == 1)))
{
Printf(TEXTCOLOR_ORANGE "Unsupported color format in %s\n", Name.GetChars());
}
else
{
jpeg_start_decompress(&cinfo);
int yc = 0;
TArray<uint8_t> buff(cinfo.output_height * cinfo.output_width * cinfo.output_components, true);
while (cinfo.output_scanline < cinfo.output_height)
{
uint8_t * ptr = buff.Data() + cinfo.output_width * cinfo.output_components * yc;
jpeg_read_scanlines(&cinfo, &ptr, 1);
yc++;
}
switch (cinfo.out_color_space)
{
case JCS_RGB:
bmp->CopyPixelDataRGB(0, 0, buff.Data(), cinfo.output_width, cinfo.output_height,
3, cinfo.output_width * cinfo.output_components, 0, CF_RGB);
break;
case JCS_GRAYSCALE:
for (int i = 0; i < 256; i++) pe[i] = PalEntry(255, i, i, i); // default to a gray map
bmp->CopyPixelData(0, 0, buff.Data(), cinfo.output_width, cinfo.output_height,
1, cinfo.output_width, 0, pe);
break;
case JCS_CMYK:
bmp->CopyPixelDataRGB(0, 0, buff.Data(), cinfo.output_width, cinfo.output_height,
4, cinfo.output_width * cinfo.output_components, 0, CF_CMYK);
break;
case JCS_YCbCr:
bmp->CopyPixelDataRGB(0, 0, buff.Data(), cinfo.output_width, cinfo.output_height,
4, cinfo.output_width * cinfo.output_components, 0, CF_YCbCr);
break;
default:
assert(0);
break;
}
jpeg_finish_decompress(&cinfo);
}
}
catch (int)
{
Printf(TEXTCOLOR_ORANGE "JPEG error in %s\n", Name.GetChars());
}
jpeg_destroy_decompress(&cinfo);
return 0;
}

View file

@ -0,0 +1,412 @@
/*
** pcxtexture.cpp
** Texture class for PCX images
**
**---------------------------------------------------------------------------
** Copyright 2005 David HENRY
** 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 "basics.h"
#include "files.h"
#include "bitmap.h"
#include "image.h"
#include "cache1d.h"
//==========================================================================
//
// PCX file header
//
//==========================================================================
#pragma pack(1)
struct PCXHeader
{
uint8_t manufacturer;
uint8_t version;
uint8_t encoding;
uint8_t bitsPerPixel;
uint16_t xmin, ymin;
uint16_t xmax, ymax;
uint16_t horzRes, vertRes;
uint8_t palette[48];
uint8_t reserved;
uint8_t numColorPlanes;
uint16_t bytesPerScanLine;
uint16_t paletteType;
uint16_t horzSize, vertSize;
uint8_t padding[54];
} FORCE_PACKED;
#pragma pack()
//==========================================================================
//
// a PCX texture
//
//==========================================================================
class FPCXTexture : public FImageSource
{
public:
FPCXTexture (PCXHeader &);
int CopyPixels(FBitmap *bmp, int conversion) override;
protected:
void ReadPCX1bit (uint8_t *dst, FileReader & lump, PCXHeader *hdr);
void ReadPCX4bits (uint8_t *dst, FileReader & lump, PCXHeader *hdr);
void ReadPCX8bits (uint8_t *dst, FileReader & lump, PCXHeader *hdr);
void ReadPCX24bits (uint8_t *dst, FileReader & lump, PCXHeader *hdr, int planes);
};
//==========================================================================
//
//
//
//==========================================================================
FImageSource * PCXImage_TryCreate(FileReader & file)
{
PCXHeader hdr;
file.Seek(0, FileReader::SeekSet);
if (file.Read(&hdr, sizeof(hdr)) != sizeof(hdr))
{
return NULL;
}
hdr.xmin = LittleShort(hdr.xmin);
hdr.xmax = LittleShort(hdr.xmax);
hdr.bytesPerScanLine = LittleShort(hdr.bytesPerScanLine);
if (hdr.manufacturer != 10 || hdr.encoding != 1) return NULL;
if (hdr.version != 0 && hdr.version != 2 && hdr.version != 3 && hdr.version != 4 && hdr.version != 5) return NULL;
if (hdr.bitsPerPixel != 1 && hdr.bitsPerPixel != 8 && hdr.bitsPerPixel != 4) return NULL;
if (hdr.bitsPerPixel == 1 && hdr.numColorPlanes !=1 && hdr.numColorPlanes != 4) return NULL;
if (hdr.bitsPerPixel == 8 && hdr.bytesPerScanLine != ((hdr.xmax - hdr.xmin + 2)&~1)) return NULL;
for (int i = 0; i < 54; i++)
{
if (hdr.padding[i] != 0) return NULL;
}
file.Seek(0, FileReader::SeekSet);
file.Read(&hdr, sizeof(hdr));
return new FPCXTexture(hdr);
}
//==========================================================================
//
//
//
//==========================================================================
FPCXTexture::FPCXTexture(PCXHeader & hdr)
{
bMasked = false;
Width = LittleShort(hdr.xmax) - LittleShort(hdr.xmin) + 1;
Height = LittleShort(hdr.ymax) - LittleShort(hdr.ymin) + 1;
}
//==========================================================================
//
//
//
//==========================================================================
void FPCXTexture::ReadPCX1bit (uint8_t *dst, FileReader & lump, PCXHeader *hdr)
{
int y, i, bytes;
int rle_count = 0;
uint8_t rle_value = 0;
TArray<uint8_t> srcp = lump.Read(lump.GetLength() - sizeof(PCXHeader));
uint8_t * src = srcp.Data();
for (y = 0; y < Height; ++y)
{
uint8_t * ptr = &dst[y * Width];
bytes = hdr->bytesPerScanLine;
while (bytes--)
{
if (rle_count == 0)
{
if ( (rle_value = *src++) < 0xc0)
{
rle_count = 1;
}
else
{
rle_count = rle_value - 0xc0;
rle_value = *src++;
}
}
rle_count--;
for (i = 7; i >= 0; --i, ptr ++)
{
// This can overflow for the last byte if not checked.
if (ptr < dst+Width*Height)
*ptr = ((rle_value & (1 << i)) > 0);
}
}
}
}
//==========================================================================
//
//
//
//==========================================================================
void FPCXTexture::ReadPCX4bits (uint8_t *dst, FileReader & lump, PCXHeader *hdr)
{
int rle_count = 0, rle_value = 0;
int x, y, c;
int bytes;
TArray<uint8_t> line(hdr->bytesPerScanLine, true);
TArray<uint8_t> colorIndex(Width, true);
TArray<uint8_t> srcp = lump.Read(lump.GetLength() - sizeof(PCXHeader));
uint8_t * src = srcp.Data();
for (y = 0; y < Height; ++y)
{
uint8_t * ptr = &dst[y * Width];
memset (ptr, 0, Width * sizeof (uint8_t));
for (c = 0; c < 4; ++c)
{
uint8_t * pLine = line.Data();
bytes = hdr->bytesPerScanLine;
while (bytes--)
{
if (rle_count == 0)
{
if ( (rle_value = *src++) < 0xc0)
{
rle_count = 1;
}
else
{
rle_count = rle_value - 0xc0;
rle_value = *src++;
}
}
rle_count--;
*(pLine++) = rle_value;
}
}
/* compute line's color indexes */
for (x = 0; x < Width; ++x)
{
if (line[x / 8] & (128 >> (x % 8)))
ptr[x] += (1 << c);
}
}
}
//==========================================================================
//
//
//
//==========================================================================
void FPCXTexture::ReadPCX8bits (uint8_t *dst, FileReader & lump, PCXHeader *hdr)
{
int rle_count = 0, rle_value = 0;
int y, bytes;
auto srcp = lump.Read(lump.GetLength() - sizeof(PCXHeader));
uint8_t * src = srcp.Data();
for (y = 0; y < Height; ++y)
{
uint8_t * ptr = &dst[y * Width];
bytes = hdr->bytesPerScanLine;
while (bytes--)
{
if (rle_count == 0)
{
if( (rle_value = *src++) < 0xc0)
{
rle_count = 1;
}
else
{
rle_count = rle_value - 0xc0;
rle_value = *src++;
}
}
rle_count--;
*ptr++ = rle_value;
}
}
}
//==========================================================================
//
//
//
//==========================================================================
void FPCXTexture::ReadPCX24bits (uint8_t *dst, FileReader & lump, PCXHeader *hdr, int planes)
{
int rle_count = 0, rle_value = 0;
int y, c;
int bytes;
auto srcp = lump.Read(lump.GetLength() - sizeof(PCXHeader));
uint8_t * src = srcp.Data();
for (y = 0; y < Height; ++y)
{
/* for each color plane */
for (c = 0; c < planes; ++c)
{
uint8_t * ptr = &dst[y * Width * planes];
bytes = hdr->bytesPerScanLine;
while (bytes--)
{
if (rle_count == 0)
{
if( (rle_value = *src++) < 0xc0)
{
rle_count = 1;
}
else
{
rle_count = rle_value - 0xc0;
rle_value = *src++;
}
}
rle_count--;
ptr[c] = (uint8_t)rle_value;
ptr += planes;
}
}
}
}
//===========================================================================
//
// FPCXTexture::CopyPixels
//
// Preserves the full color information (unlike software mode)
//
//===========================================================================
int FPCXTexture::CopyPixels(FBitmap *bmp, int conversion)
{
PalEntry pe[256];
PCXHeader header;
int bitcount;
TArray<uint8_t> Pixels;
auto lump = kopenFileReader(Name, 0);
if (!lump.isOpen()) return -1; // Just leave the texture blank.
lump.Read(&header, sizeof(header));
bitcount = header.bitsPerPixel * header.numColorPlanes;
if (bitcount < 24)
{
Pixels.Resize(Width*Height);
if (bitcount < 8)
{
switch (bitcount)
{
default:
case 1:
pe[0] = PalEntry(255, 0, 0, 0);
pe[1] = PalEntry(255, 255, 255, 255);
ReadPCX1bit (Pixels.Data(), lump, &header);
break;
case 4:
for (int i = 0; i<16; i++)
{
pe[i] = PalEntry(255, header.palette[i * 3], header.palette[i * 3 + 1], header.palette[i * 3 + 2]);
}
ReadPCX4bits (Pixels.Data(), lump, &header);
break;
}
}
else if (bitcount == 8)
{
lump.Seek(-769, FileReader::SeekEnd);
uint8_t c = lump.ReadUInt8();
c=0x0c; // Apparently there's many non-compliant PCXs out there...
if (c !=0x0c)
{
for(int i=0;i<256;i++) pe[i]=PalEntry(255,i,i,i); // default to a gray map
}
else for(int i=0;i<256;i++)
{
uint8_t r = lump.ReadUInt8();
uint8_t g = lump.ReadUInt8();
uint8_t b = lump.ReadUInt8();
pe[i] = PalEntry(255, r,g,b);
}
lump.Seek(sizeof(header), FileReader::SeekSet);
ReadPCX8bits (Pixels.Data(), lump, &header);
}
bmp->CopyPixelData(0, 0, Pixels.Data(), Width, Height, 1, Width, 0, pe);
}
else
{
Pixels.Resize(Width*Height*4);
ReadPCX24bits (Pixels.Data(), lump, &header, 3);
bmp->CopyPixelDataRGB(0, 0, Pixels.Data(), Width, Height, 3, Width*3, 0, CF_RGB);
}
return 0;
}

View file

@ -0,0 +1,364 @@
/*
** pngtexture.cpp
** Texture class for PNG images
**
**---------------------------------------------------------------------------
** Copyright 2004-2007 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 "files.h"
#include "templates.h"
#include "m_png.h"
#include "bitmap.h"
#include "image.h"
#include "printf.h"
#include "cache1d.h"
//==========================================================================
//
// A PNG texture
//
//==========================================================================
class FPNGTexture : public FImageSource
{
public:
FPNGTexture (FileReader &lump, int width, int height, uint8_t bitdepth, uint8_t colortype, uint8_t interlace);
int CopyPixels(FBitmap *bmp, int conversion) override;
protected:
uint8_t BitDepth;
uint8_t ColorType;
uint8_t Interlace;
bool HaveTrans;
uint16_t NonPaletteTrans[3];
int PaletteSize = 0;
uint32_t StartOfIDAT = 0;
uint32_t StartOfPalette = 0;
};
//==========================================================================
//
//
//
//==========================================================================
FImageSource *PNGImage_TryCreate(FileReader & data)
{
union
{
uint32_t dw;
uint16_t w[2];
uint8_t b[4];
} first4bytes;
// 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(0, FileReader::SeekSet);
if (data.Read (first4bytes.b, 4) != 4) return NULL;
if (first4bytes.dw != MAKE_ID(137,'P','N','G')) return NULL;
if (data.Read (first4bytes.b, 4) != 4) return NULL;
if (first4bytes.dw != MAKE_ID(13,10,26,10)) return NULL;
if (data.Read (first4bytes.b, 4) != 4) return NULL;
if (first4bytes.dw != MAKE_ID(0,0,0,13)) return NULL;
if (data.Read (first4bytes.b, 4) != 4) return NULL;
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.
int width = data.ReadInt32BE();
int height = data.ReadInt32BE();
uint8_t bitdepth = data.ReadUInt8();
uint8_t colortype = data.ReadUInt8();
uint8_t compression = data.ReadUInt8();
uint8_t filter = data.ReadUInt8();
uint8_t interlace = data.ReadUInt8();
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, FileReader::SeekSet);
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, width, height, bitdepth, colortype, interlace);
}
//==========================================================================
//
//
//
//==========================================================================
FPNGTexture::FPNGTexture (FileReader &lump, int width, int height,
uint8_t depth, uint8_t colortype, uint8_t interlace)
: BitDepth(depth), ColorType(colortype), Interlace(interlace), HaveTrans(false)
{
uint8_t trans[256];
uint32_t len, id;
bMasked = false;
Width = width;
Height = height;
memset(trans, 255, 256);
// Parse pre-IDAT chunks. I skip the CRCs. Is that bad?
lump.Seek(33, FileReader::SeekSet);
lump.Read(&len, 4);
lump.Read(&id, 4);
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, FileReader::SeekCur);
break;
case MAKE_ID('g','r','A','b'):
// This is like GRAB found in an ILBM, except coordinates use 4 bytes
{
uint32_t hotx, hoty;
int ihotx, ihoty;
lump.Read(&hotx, 4);
lump.Read(&hoty, 4);
ihotx = BigLong((int)hotx);
ihoty = BigLong((int)hoty);
if (ihotx < -32768 || ihotx > 32767)
{
Printf ("X-Offset for PNG texture %s is bad: %d (0x%08x)\n", Name.GetChars(), ihotx, ihotx);
ihotx = 0;
}
if (ihoty < -32768 || ihoty > 32767)
{
Printf ("Y-Offset for PNG texture %s is bad: %d (0x%08x)\n", Name.GetChars(), ihoty, ihoty);
ihoty = 0;
}
LeftOffset = ihotx;
TopOffset = ihoty;
}
break;
case MAKE_ID('P','L','T','E'):
PaletteSize = std::min<int> (len / 3, 256);
StartOfPalette = (uint32_t)lump.Tell();
break;
case MAKE_ID('t','R','N','S'):
lump.Read (trans, len);
HaveTrans = true;
// Save for colortype 2
NonPaletteTrans[0] = uint16_t(trans[0] * 256 + trans[1]);
NonPaletteTrans[1] = uint16_t(trans[2] * 256 + trans[3]);
NonPaletteTrans[2] = uint16_t(trans[4] * 256 + trans[5]);
break;
}
lump.Seek(4, FileReader::SeekCur); // Skip CRC
lump.Read(&len, 4);
id = MAKE_ID('I','E','N','D');
lump.Read(&id, 4);
}
StartOfIDAT = (uint32_t)lump.Tell() - 8;
switch (colortype)
{
case 4: // Grayscale + Alpha
bMasked = true;
break;
case 0: // Grayscale
if (colortype == 0 && HaveTrans && NonPaletteTrans[0] < 256)
{
bMasked = true;
}
break;
case 3: // Paletted
break;
case 6: // RGB + Alpha
bMasked = true;
break;
case 2: // RGB
bMasked = HaveTrans;
break;
}
}
//===========================================================================
//
// FPNGTexture::CopyPixels
//
//===========================================================================
int FPNGTexture::CopyPixels(FBitmap *bmp, int conversion)
{
// Parse pre-IDAT chunks. I skip the CRCs. Is that bad?
PalEntry pe[256];
uint32_t len, id;
static char bpp[] = {1, 0, 3, 1, 2, 0, 4};
int pixwidth = Width * bpp[ColorType];
int transpal = false;
FileReader *lump;
FileReader lfr;
lfr = kopenFileReader(Name, 0);
if (!lfr.isOpen()) return -1; // Just leave the texture blank.
lump = &lfr;
lump->Seek(33, FileReader::SeekSet);
for(int i = 0; i < 256; i++) // default to a gray map
pe[i] = PalEntry(255,i,i,i);
lump->Read(&len, 4);
lump->Read(&id, 4);
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, FileReader::SeekCur);
break;
case MAKE_ID('P','L','T','E'):
for(int i = 0; i < PaletteSize; i++)
{
pe[i].r = lump->ReadUInt8();
pe[i].g = lump->ReadUInt8();
pe[i].b = lump->ReadUInt8();
}
break;
case MAKE_ID('t','R','N','S'):
if (ColorType == 3)
{
for(uint32_t i = 0; i < len; i++)
{
pe[i].a = lump->ReadUInt8();
if (pe[i].a != 0 && pe[i].a != 255)
transpal = true;
}
}
else
{
lump->Seek(len, FileReader::SeekCur);
}
break;
}
lump->Seek(4, FileReader::SeekCur); // Skip CRC
lump->Read(&len, 4);
id = MAKE_ID('I','E','N','D');
lump->Read(&id, 4);
}
if (ColorType == 0 && HaveTrans && NonPaletteTrans[0] < 256)
{
pe[NonPaletteTrans[0]].a = 0;
transpal = true;
}
uint8_t * Pixels = new uint8_t[pixwidth * Height];
lump->Seek (StartOfIDAT, FileReader::SeekSet);
lump->Read(&len, 4);
lump->Read(&id, 4);
M_ReadIDAT (*lump, Pixels, Width, Height, pixwidth, BitDepth, ColorType, Interlace, BigLong((unsigned int)len));
switch (ColorType)
{
case 0:
case 3:
bmp->CopyPixelData(0, 0, Pixels, Width, Height, 1, Width, 0, pe);
break;
case 2:
if (!HaveTrans)
{
bmp->CopyPixelDataRGB(0, 0, Pixels, Width, Height, 3, pixwidth, 0, CF_RGB);
}
else
{
bmp->CopyPixelDataRGB(0, 0, Pixels, Width, Height, 3, pixwidth, 0, CF_RGBT,
NonPaletteTrans[0], NonPaletteTrans[1], NonPaletteTrans[2]);
transpal = true;
}
break;
case 4:
bmp->CopyPixelDataRGB(0, 0, Pixels, Width, Height, 2, pixwidth, 0, CF_IA);
transpal = -1;
break;
case 6:
bmp->CopyPixelDataRGB(0, 0, Pixels, Width, Height, 4, pixwidth, 0, CF_RGBA);
transpal = -1;
break;
default:
break;
}
delete[] Pixels;
return transpal;
}

View file

@ -0,0 +1,319 @@
/*
** tgatexture.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 "files.h"
#include "templates.h"
#include "bitmap.h"
#include "image.h"
#include "cache1d.h"
//==========================================================================
//
// TGA file header
//
//==========================================================================
#pragma pack(1)
struct TGAHeader
{
uint8_t id_len;
uint8_t has_cm;
uint8_t img_type;
int16_t cm_first;
int16_t cm_length;
uint8_t cm_size;
int16_t x_origin;
int16_t y_origin;
int16_t width;
int16_t height;
uint8_t bpp;
uint8_t img_desc;
};
#pragma pack()
//==========================================================================
//
// a TGA texture
//
//==========================================================================
class FTGATexture : public FImageSource
{
public:
FTGATexture (TGAHeader *);
int CopyPixels(FBitmap *bmp, int conversion) override;
protected:
void ReadCompressed(FileReader &lump, uint8_t * buffer, int bytesperpixel);
};
//==========================================================================
//
//
//
//==========================================================================
FImageSource *TGAImage_TryCreate(FileReader & file)
{
TGAHeader hdr;
if (file.GetLength() < (long)sizeof(hdr)) return NULL;
file.Seek(0, FileReader::SeekSet);
file.Read(&hdr, sizeof(hdr));
hdr.width = LittleShort(hdr.width);
hdr.height = LittleShort(hdr.height);
// 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 NULL;
if (hdr.width <=0 || hdr.height <=0 || hdr.width > 2048 || hdr.height > 2048) return NULL;
if (hdr.bpp != 8 && hdr.bpp != 15 && hdr.bpp != 16 && hdr.bpp !=24 && hdr.bpp !=32) return NULL;
if (hdr.img_type <= 0 || hdr.img_type > 11) return NULL;
if (hdr.img_type >=4 && hdr.img_type <= 8) return NULL;
if ((hdr.img_desc & 16) != 0) return NULL;
file.Seek(0, FileReader::SeekSet);
file.Read(&hdr, sizeof(hdr));
hdr.width = LittleShort(hdr.width);
hdr.height = LittleShort(hdr.height);
return new FTGATexture(&hdr);
}
//==========================================================================
//
//
//
//==========================================================================
FTGATexture::FTGATexture(TGAHeader * hdr)
{
Width = hdr->width;
Height = hdr->height;
// Alpha channel is used only for 32 bit RGBA and paletted images with RGBA palettes.
bMasked = (hdr->img_desc&15)==8 && (hdr->bpp==32 || (hdr->img_type==1 && hdr->cm_size==32));
}
//==========================================================================
//
//
//
//==========================================================================
void FTGATexture::ReadCompressed(FileReader &lump, uint8_t * buffer, int bytesperpixel)
{
uint8_t data[4];
int Size = Width * Height;
while (Size > 0)
{
uint8_t b = lump.ReadUInt8();
if (b & 128)
{
b&=~128;
lump.Read(data, bytesperpixel);
for (int i=std::min<int>(Size, (b+1)); i>0; i--)
{
buffer[0] = data[0];
if (bytesperpixel>=2) buffer[1] = data[1];
if (bytesperpixel>=3) buffer[2] = data[2];
if (bytesperpixel==4) buffer[3] = data[3];
buffer+=bytesperpixel;
}
}
else
{
lump.Read(buffer, std::min<int>(Size, (b+1))*bytesperpixel);
buffer += (b+1)*bytesperpixel;
}
Size -= b+1;
}
}
//===========================================================================
//
// FTGATexture::CopyPixels
//
//===========================================================================
int FTGATexture::CopyPixels(FBitmap *bmp, int conversion)
{
PalEntry pe[256];
auto lump = kopenFileReader(Name, 0);
TGAHeader hdr;
uint16_t w;
uint8_t r,g,b,a;
int transval = 0;
lump.Read(&hdr, sizeof(hdr));
lump.Seek(hdr.id_len, FileReader::SeekCur);
hdr.width = LittleShort(hdr.width);
hdr.height = LittleShort(hdr.height);
hdr.cm_first = LittleShort(hdr.cm_first);
hdr.cm_length = LittleShort(hdr.cm_length);
if (hdr.has_cm)
{
memset(pe, 0, 256*sizeof(PalEntry));
for (int i = hdr.cm_first; i < hdr.cm_first + hdr.cm_length && i < 256; i++)
{
switch (hdr.cm_size)
{
case 15:
case 16:
w = lump.ReadUInt16();
r = (w & 0x001F) << 3;
g = (w & 0x03E0) >> 2;
b = (w & 0x7C00) >> 7;
a = 255;
break;
case 24:
b = lump.ReadUInt8();
g = lump.ReadUInt8();
r = lump.ReadUInt8();
a = 255;
break;
case 32:
b = lump.ReadUInt8();
g = lump.ReadUInt8();
r = lump.ReadUInt8();
a = lump.ReadUInt8();
if ((hdr.img_desc & 15) != 8) a = 255;
else if (a != 0 && a != 255) transval = true;
break;
default: // should never happen
r=g=b=a=0;
break;
}
pe[i] = PalEntry(a, r, g, b);
}
}
int Size = Width * Height * (hdr.bpp>>3);
TArray<uint8_t> sbuffer(Size);
if (hdr.img_type < 4) // uncompressed
{
lump.Read(sbuffer.Data(), Size);
}
else // compressed
{
ReadCompressed(lump, sbuffer.Data(), hdr.bpp>>3);
}
uint8_t * ptr = sbuffer.Data();
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&32))
{
ptr += (Height-1) * Pitch;
Pitch = -Pitch;
}
switch (hdr.img_type & 7)
{
case 1: // paletted
bmp->CopyPixelData(0, 0, ptr, Width, Height, step_x, Pitch, 0, pe);
break;
case 2: // RGB
switch (hdr.bpp)
{
case 15:
case 16:
bmp->CopyPixelDataRGB(0, 0, ptr, Width, Height, step_x, Pitch, 0, CF_RGB555);
break;
case 24:
bmp->CopyPixelDataRGB(0, 0, ptr, Width, Height, step_x, Pitch, 0, CF_BGR);
break;
case 32:
if ((hdr.img_desc&15)!=8) // 32 bits without a valid alpha channel
{
bmp->CopyPixelDataRGB(0, 0, ptr, Width, Height, step_x, Pitch, 0, CF_BGR);
}
else
{
bmp->CopyPixelDataRGB(0, 0, ptr, Width, Height, step_x, Pitch, 0, CF_BGRA);
transval = -1;
}
break;
default:
break;
}
break;
case 3: // Grayscale
switch (hdr.bpp)
{
case 8:
for(int i=0;i<256;i++) pe[i]=PalEntry(255,i,i,i); // gray map
bmp->CopyPixelData(0, 0, ptr, Width, Height, step_x, Pitch, 0, pe);
break;
case 16:
bmp->CopyPixelDataRGB(0, 0, ptr, Width, Height, step_x, Pitch, 0, CF_I16);
break;
default:
break;
}
break;
default:
break;
}
return transval;
}

View file

@ -0,0 +1,130 @@
/*
** texture.cpp
** The base texture class
**
**---------------------------------------------------------------------------
** Copyright 2004-2007 Randy Heit
** Copyright 2006-2018 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 "memarena.h"
#include "bitmap.h"
#include "image.h"
#include "files.h"
#include "cache1d.h"
int FImageSource::NextID;
//===========================================================================
//
// the default just returns an empty texture.
//
//===========================================================================
TArray<uint8_t> FImageSource::CreatePalettedPixels(int conversion)
{
TArray<uint8_t> Pixels(Width * Height, true);
memset(Pixels.Data(), 0, Width * Height);
return Pixels;
}
//===========================================================================
//
// FImageSource::CopyPixels
//
// this is the generic case that can handle
// any properly implemented texture for software rendering.
// Its drawback is that it is limited to the base palette which is
// why all classes that handle different palettes should subclass this
// method
//
//===========================================================================
int FImageSource::CopyTranslatedPixels(FBitmap *bmp, PalEntry *remap)
{
auto ppix = CreatePalettedPixels(false);
bmp->CopyPixelData(0, 0, ppix.Data(), Width, Height, Height, 1, 0, remap);
return 0;
}
int FImageSource::CopyPixels(FBitmap* bmp, int conversion)
{
return CopyTranslatedPixels(bmp, nullptr); // This should never get called for ART tiles.
}
//==========================================================================
//
//
//
//==========================================================================
typedef FImageSource * (*CreateFunc)(FileReader & file);
struct TexCreateInfo
{
CreateFunc TryCreate;
};
FImageSource *PNGImage_TryCreate(FileReader &);
FImageSource *JPEGImage_TryCreate(FileReader &);
FImageSource *DDSImage_TryCreate(FileReader &);
FImageSource *PCXImage_TryCreate(FileReader &);
FImageSource *TGAImage_TryCreate(FileReader &);
// Examines the lump contents to decide what type of texture to create,
// and creates the texture.
FImageSource * FImageSource::GetImage(const char *name)
{
static TexCreateInfo CreateInfo[] = {
{ PNGImage_TryCreate },
{ JPEGImage_TryCreate },
{ DDSImage_TryCreate },
{ PCXImage_TryCreate },
{ TGAImage_TryCreate },
{ nullptr }
};
auto data = kopenFileReader(name, 0);
if (!data.isOpen()) return nullptr;
for (size_t i = 0; CreateInfo[i].TryCreate; i++)
{
auto image = CreateInfo[i].TryCreate(data);
if (image != nullptr)
{
image->Name = name;
return image;
}
}
return nullptr;
}

View file

@ -0,0 +1,138 @@
#pragma once
#include <stdint.h>
#include "zstring.h"
#include "tarray.h"
#include "textures.h"
#include "bitmap.h"
#include "memarena.h"
class FImageSource;
using PrecacheInfo = TMap<int, std::pair<int, int>>;
struct PalettedPixels
{
friend class FImageSource;
TArrayView<uint8_t> Pixels;
private:
TArray<uint8_t> PixelStore;
bool ownsPixels() const
{
return Pixels.Data() == PixelStore.Data();
}
};
// This represents a naked image. It has no high level logic attached to it.
// All it can do is provide raw image data to its users.
class FImageSource
{
protected:
static int NextID;
int SourceLump;
int Width = 0, Height = 0;
int LeftOffset = 0, TopOffset = 0; // Offsets stored in the image.
bool bUseGamePalette = false; // true if this is an image without its own color set.
int ImageID = -1;
FString Name;
// Internal image creation functions. All external access should go through the cache interface,
// so that all code can benefit from future improvements to that.
public:
virtual ~FImageSource() = default;
void CopySize(FImageSource &other)
{
Width = other.Width;
Height = other.Height;
LeftOffset = other.LeftOffset;
TopOffset = other.TopOffset;
SourceLump = other.SourceLump;
}
bool bMasked = true; // Image (might) have holes (Assume true unless proven otherwise!)
int8_t bTranslucent = -1; // Image has pixels with a non-0/1 value. (-1 means the user needs to do a real check)
int GetId() const { return ImageID; }
// 'noremap0' will only be looked at by FPatchTexture and forwarded by FMultipatchTexture.
static FImageSource * GetImage(const char *name);
virtual TArray<uint8_t> CreatePalettedPixels(int conversion);
virtual int CopyPixels(FBitmap* bmp, int conversion); // This will always ignore 'luminance'.
int CopyTranslatedPixels(FBitmap* bmp, PalEntry* remap);
// Conversion option
enum EType
{
normal = 0,
luminance = 1,
noremap0 = 2
};
FImageSource(int sourcelump = -1) : SourceLump(sourcelump) { ImageID = ++NextID; }
int GetWidth() const
{
return Width;
}
int GetHeight() const
{
return Height;
}
std::pair<int, int> GetSize() const
{
return std::make_pair(Width, Height);
}
std::pair<int, int> GetOffsets() const
{
return std::make_pair(LeftOffset, TopOffset);
}
void SetOffsets(int x, int y)
{
LeftOffset = x;
TopOffset = y;
}
int LumpNum() const
{
return SourceLump;
}
bool UseGamePalette() const
{
return bUseGamePalette;
}
};
//==========================================================================
//
// a TGA texture
//
//==========================================================================
class FImageTexture : public FTexture
{
FImageSource *mImage;
public:
FImageTexture (FImageSource *image, const char *name = nullptr);
virtual TArray<uint8_t> Get8BitPixels(bool alphatex);
void SetImage(FImageSource *img) // This is only for the multipatch texture builder!
{
mImage = img;
}
FImageSource *GetImage() const override { return mImage; }
FBitmap GetBgraBitmap(PalEntry *p, int *trans) override;
};

View file

@ -0,0 +1,89 @@
/*
** imagetexture.cpp
** Texture class based on FImageSource
**
**---------------------------------------------------------------------------
** Copyright 2018 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 "files.h"
#include "templates.h"
#include "bitmap.h"
#include "image.h"
//==========================================================================
//
//
//
//==========================================================================
FImageTexture::FImageTexture(FImageSource *img, const char *name)
: FTexture(name)
{
mImage = img;
if (img != nullptr)
{
Width = img->GetWidth();
Height = img->GetHeight();
auto offsets = img->GetOffsets();
LeftOffset = offsets.first;
TopOffset = offsets.second;
bMasked = img->bMasked;
bTranslucent = img->bTranslucent;
}
}
//===========================================================================
//
//
//
//===========================================================================
FBitmap FImageTexture::GetBgraBitmap(PalEntry *p, int *trans)
{
FBitmap bmp;
mImage->CopyPixels(&bmp, 0); // Todo: Handle translations.
return bmp;
}
//===========================================================================
//
//
//
//===========================================================================
TArray<uint8_t> FImageTexture::Get8BitPixels(bool alpha)
{
return mImage->CreatePalettedPixels(0);
}

View file

@ -0,0 +1,63 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2004-2016 Christoph Oelckers
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
#include "filesystem.h"
#include "textures.h"
#include "skyboxtexture.h"
#include "bitmap.h"
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
FSkyBox::FSkyBox(const char *name)
: FTexture(name)
{
bSkybox = true;
fliptop = false;
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
FBitmap FSkyBox::GetBgraBitmap(PalEntry *p, int *trans)
{
return faces[0]->GetBgraBitmap(p, trans);
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
FImageSource *FSkyBox::GetImage() const
{
return faces[0]->GetImage();
}

View file

@ -0,0 +1,37 @@
#pragma once
#include "textures.h"
//-----------------------------------------------------------------------------
//
// This is not a real texture but will be added to the texture manager
// so that it can be handled like any other sky.
//
//-----------------------------------------------------------------------------
class FSkyBox : public FTexture
{
public:
FTexture* faces[6] = {};
bool fliptop;
FSkyBox(const char *name);
FBitmap GetBgraBitmap(PalEntry *, int *trans) override;
FImageSource *GetImage() const override;
void SetSize()
{
CopySize(faces[0]);
}
bool Is3Face() const
{
return faces[5] == nullptr;
}
bool IsFlipped() const
{
return fliptop;
}
};

View file

@ -0,0 +1,419 @@
/*
** texture.cpp
** The base texture class
**
**---------------------------------------------------------------------------
** Copyright 2004-2007 Randy Heit
** Copyright 2006-2018 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 "files.h"
#include "templates.h"
#include "bitmap.h"
#include "image.h"
FTexture *CreateBrightmapTexture(FImageSource*);
//==========================================================================
//
//
//
//==========================================================================
// Examines the lump contents to decide what type of texture to create,
// and creates the texture.
FTexture * FTexture::CreateTexture(const char *name)
{
auto image = FImageSource::GetImage(name);
if (image != nullptr)
{
FTexture *tex = new FImageTexture(image);
if (tex != nullptr)
{
tex->Name = name;
return tex;
}
}
return nullptr;
}
//==========================================================================
//
//
//
//==========================================================================
FTexture::FTexture (const char *name)
{
Name = name;
}
FTexture::~FTexture ()
{
}
//===========================================================================
//
// FTexture::GetBgraBitmap
//
// Default returns just an empty bitmap. This needs to be overridden by
// any subclass that actually does return a software pixel buffer.
//
//===========================================================================
FBitmap FTexture::GetBgraBitmap(PalEntry *remap, int *ptrans)
{
FBitmap bmp;
bmp.Create(Width, Height);
return bmp;
}
//===========================================================================
//
// Gets the average color of a texture for use as a sky cap color
//
//===========================================================================
#define APART(c) (((c)>>24)&0xff)
#define RPART(c) (((c)>>16)&0xff)
#define GPART(c) (((c)>>8)&0xff)
#define BPART(c) ((c)&0xff)
PalEntry FTexture::averageColor(const uint32_t *data, int size, int maxout)
{
int i;
unsigned int r, g, b;
// First clear them.
r = g = b = 0;
if (size == 0)
{
return PalEntry(255, 255, 255);
}
for (i = 0; i < size; i++)
{
b += BPART(data[i]);
g += GPART(data[i]);
r += RPART(data[i]);
}
r = r / size;
g = g / size;
b = b / size;
int maxv = std::max(std::max(r, g), b);
if (maxv && maxout)
{
r = uint64_t(r) * maxout / maxv;
g = uint64_t(g) * maxout / maxv;
b = uint64_t(b) * maxout / maxv;
}
return PalEntry(255, r, g, b);
}
PalEntry FTexture::GetSkyCapColor(bool bottom)
{
if (!skyColorDone)
{
skyColorDone = true;
FBitmap bitmap = GetBgraBitmap(nullptr);
int w = bitmap.GetWidth();
int h = bitmap.GetHeight();
const uint32_t *buffer = (const uint32_t *)bitmap.GetPixels();
if (buffer)
{
CeilingSkyColor = averageColor((uint32_t *)buffer, w * std::min(30, h), 0);
if (h>30)
{
FloorSkyColor = averageColor(((uint32_t *)buffer) + (h - 30)*w, w * 30, 0);
}
else FloorSkyColor = CeilingSkyColor;
}
}
return bottom ? FloorSkyColor : CeilingSkyColor;
}
//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------
void FTexture::CheckTrans(unsigned char * buffer, int size, int trans)
{
if (bTranslucent == -1)
{
bTranslucent = trans;
if (trans == -1)
{
uint32_t * dwbuf = (uint32_t*)buffer;
for (int i = 0; i<size; i++)
{
uint32_t alpha = dwbuf[i] >> 24;
if (alpha != 0xff && alpha != 0)
{
bTranslucent = 1;
return;
}
}
bTranslucent = 0;
}
}
}
//===========================================================================
//
// smooth the edges of transparent fields in the texture
//
//===========================================================================
#ifdef WORDS_BIGENDIAN
#define MSB 0
#define SOME_MASK 0xffffff00
#else
#define MSB 3
#define SOME_MASK 0x00ffffff
#endif
#define CHKPIX(ofs) (l1[(ofs)*4+MSB]==255 ? (( ((uint32_t*)l1)[0] = ((uint32_t*)l1)[ofs]&SOME_MASK), trans=true ) : false)
int FTexture::SmoothEdges(unsigned char * buffer, int w, int h)
{
int x, y;
int trans = buffer[MSB] == 0; // If I set this to false here the code won't detect textures
// that only contain transparent pixels.
int semitrans = false;
unsigned char * l1;
if (h <= 1 || w <= 1) return false; // makes (a) no sense and (b) doesn't work with this code!
l1 = buffer;
if (l1[MSB] == 0 && !CHKPIX(1)) CHKPIX(w);
else if (l1[MSB]<255) semitrans = true;
l1 += 4;
for (x = 1; x<w - 1; x++, l1 += 4)
{
if (l1[MSB] == 0 && !CHKPIX(-1) && !CHKPIX(1)) CHKPIX(w);
else if (l1[MSB]<255) semitrans = true;
}
if (l1[MSB] == 0 && !CHKPIX(-1)) CHKPIX(w);
else if (l1[MSB]<255) semitrans = true;
l1 += 4;
for (y = 1; y<h - 1; y++)
{
if (l1[MSB] == 0 && !CHKPIX(-w) && !CHKPIX(1)) CHKPIX(w);
else if (l1[MSB]<255) semitrans = true;
l1 += 4;
for (x = 1; x<w - 1; x++, l1 += 4)
{
if (l1[MSB] == 0 && !CHKPIX(-w) && !CHKPIX(-1) && !CHKPIX(1) && !CHKPIX(-w - 1) && !CHKPIX(-w + 1) && !CHKPIX(w - 1) && !CHKPIX(w + 1)) CHKPIX(w);
else if (l1[MSB]<255) semitrans = true;
}
if (l1[MSB] == 0 && !CHKPIX(-w) && !CHKPIX(-1)) CHKPIX(w);
else if (l1[MSB]<255) semitrans = true;
l1 += 4;
}
if (l1[MSB] == 0 && !CHKPIX(-w)) CHKPIX(1);
else if (l1[MSB]<255) semitrans = true;
l1 += 4;
for (x = 1; x<w - 1; x++, l1 += 4)
{
if (l1[MSB] == 0 && !CHKPIX(-w) && !CHKPIX(-1)) CHKPIX(1);
else if (l1[MSB]<255) semitrans = true;
}
if (l1[MSB] == 0 && !CHKPIX(-w)) CHKPIX(-1);
else if (l1[MSB]<255) semitrans = true;
return trans || (semitrans << 1);
}
//===========================================================================
//
// Post-process the texture data after the buffer has been created
//
//===========================================================================
bool FTexture::ProcessData(unsigned char * buffer, int w, int h, bool ispatch)
{
if (bMasked)
{
int res = SmoothEdges(buffer, w, h);
bMasked = !!(res & 1);
}
return true;
}
//===========================================================================
//
// Initializes the buffer for the texture data
//
//===========================================================================
FTextureBuffer FTexture::CreateTexBuffer(int translation, int flags)
{
FTextureBuffer result;
unsigned char * buffer = nullptr;
int W, H;
int isTransparent = -1;
bool checkonly = !!(flags & CTF_CheckOnly);
#if 0
if (flags & CTF_CheckHires)
{
// No image means that this cannot be checked,
if (GetImage() && LoadHiresTexture(result, checkonly)) return result;
}
#endif
int exx = !!(flags & CTF_Expand);
W = GetWidth() + 2 * exx;
H = GetHeight() + 2 * exx;
if (!checkonly)
{
buffer = new unsigned char[W*(H + 1) * 4];
memset(buffer, 0, W * (H + 1) * 4);
auto remap = nullptr;// translation <= 0 ? nullptr : FUniquePalette::GetPalette(translation);
FBitmap bmp(buffer, W * 4, W, H);
int trans;
auto Pixels = GetBgraBitmap(remap, &trans);
bmp.Blit(exx, exx, Pixels);
if (remap == nullptr)
{
CheckTrans(buffer, W*H, trans);
isTransparent = bTranslucent;
}
else
{
isTransparent = 0;
// A translated image is not conclusive for setting the texture's transparency info.
}
}
if (GetImage())
{
FContentIdBuilder builder;
builder.id = 0;
builder.imageID = GetImage()->GetId();
builder.translation = std::max(0, translation);
builder.expand = exx;
result.mContentId = builder.id;
}
else result.mContentId = 0; // for non-image backed textures this has no meaning so leave it at 0.
result.mBuffer = buffer;
result.mWidth = W;
result.mHeight = H;
// Only do postprocessing for image-backed textures. (i.e. not for the burn texture which can also pass through here.)
if (GetImage() && flags & CTF_ProcessData)
{
#if 0
CreateUpsampledTextureBuffer(result, !!isTransparent, checkonly);
#endif
if (!checkonly) ProcessData(result.mBuffer, result.mWidth, result.mHeight, false);
}
return result;
}
//===========================================================================
//
// Dummy texture for the 0-entry.
//
//===========================================================================
bool FTexture::GetTranslucency()
{
if (bTranslucent == -1)
{
if (true)//!bHasCanvas)
{
// This will calculate all we need, so just discard the result.
CreateTexBuffer(0);
}
/*
else
{
bTranslucent = 0;
}*/
}
return !!bTranslucent;
}
//===========================================================================
//
// the default just returns an empty texture.
//
//===========================================================================
TArray<uint8_t> FTexture::Get8BitPixels(bool alphatex)
{
TArray<uint8_t> Pixels(Width * Height, true);
memset(Pixels.Data(), 0, Width * Height);
return Pixels;
}
#if 0
//==========================================================================
//
//
//
//==========================================================================
FWrapperTexture::FWrapperTexture(int w, int h, int bits)
{
Width = w;
Height = h;
Format = bits;
UseType = ETextureType::SWCanvas;
bNoCompress = true;
auto hwtex = screen->CreateHardwareTexture();
// todo: Initialize here.
SystemTextures.AddHardwareTexture(0, false, hwtex);
}
#endif

View file

@ -0,0 +1,36 @@
#pragma once
class FTextureID
{
friend class FTextureManager;
public:
FTextureID() = default;
bool isNull() const { return texnum == 0; }
bool isValid() const { return texnum > 0; }
bool Exists() const { return texnum >= 0; }
void SetInvalid() { texnum = -1; }
void SetNull() { texnum = 0; }
bool operator ==(const FTextureID &other) const { return texnum == other.texnum; }
bool operator !=(const FTextureID &other) const { return texnum != other.texnum; }
FTextureID operator +(int offset) throw();
int GetIndex() const { return texnum; } // Use this only if you absolutely need the index!
// The switch list needs these to sort the switches by texture index
int operator -(FTextureID other) const { return texnum - other.texnum; }
bool operator < (FTextureID other) const { return texnum < other.texnum; }
bool operator > (FTextureID other) const { return texnum > other.texnum; }
protected:
FTextureID(int num) { texnum = num; }
private:
int texnum;
};
// This is for the script interface which needs to do casts from int to texture.
class FSetTextureID : public FTextureID
{
public:
FSetTextureID(int v) : FTextureID(v) {}
};

View file

@ -0,0 +1,198 @@
/*
** textures.h
**
**---------------------------------------------------------------------------
** Copyright 2005-2016 Randy Heit
** Copyright 2005-2016 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.
**---------------------------------------------------------------------------
**
*/
#ifndef __TEXTURES_H
#define __TEXTURES_H
#include "textureid.h"
#include "palentry.h"
/*
#include "v_palette.h"
#include "r_data/v_colortables.h"
#include "colormatcher.h"
#include "r_data/renderstyle.h"
#include "r_data/r_translate.h"
#include "hwrenderer/textures/hw_texcontainer.h"
*/
#include <vector>
class FImageSource;
enum ECreateTexBufferFlags
{
CTF_CheckHires = 1, // use external hires replacement if found
CTF_Expand = 2, // create buffer with a one-pixel wide border
CTF_ProcessData = 4, // run postprocessing on the generated buffer. This is only needed when using the data for a hardware texture.
CTF_CheckOnly = 8, // Only runs the code to get a content ID but does not create a texture. Can be used to access a caching system for the hardware textures.
};
class FBitmap;
struct FRemapTable;
struct FCopyInfo;
class FScanner;
// Texture IDs
class FTextureManager;
class FTerrainTypeArray;
class IHardwareTexture;
class FMaterial;
class FMultipatchTextureBuilder;
extern int r_spriteadjustSW, r_spriteadjustHW;
class FNullTextureID : public FTextureID
{
public:
FNullTextureID() : FTextureID(0) {}
};
class FGLRenderState;
class FSerializer;
namespace OpenGLRenderer
{
class FGLRenderState;
class FHardwareTexture;
}
union FContentIdBuilder
{
uint64_t id;
struct
{
unsigned imageID : 24;
unsigned translation : 16;
unsigned expand : 1;
unsigned scaler : 4;
unsigned scalefactor : 4;
};
};
struct FTextureBuffer
{
uint8_t *mBuffer = nullptr;
int mWidth = 0;
int mHeight = 0;
uint64_t mContentId = 0; // unique content identifier. (Two images created from the same image source with the same settings will return the same value.)
FTextureBuffer() = default;
~FTextureBuffer()
{
if (mBuffer) delete[] mBuffer;
}
FTextureBuffer(const FTextureBuffer &other) = delete;
FTextureBuffer(FTextureBuffer &&other)
{
mBuffer = other.mBuffer;
mWidth = other.mWidth;
mHeight = other.mHeight;
mContentId = other.mContentId;
other.mBuffer = nullptr;
}
FTextureBuffer& operator=(FTextureBuffer &&other)
{
mBuffer = other.mBuffer;
mWidth = other.mWidth;
mHeight = other.mHeight;
mContentId = other.mContentId;
other.mBuffer = nullptr;
return *this;
}
};
// Base texture class
class FTexture
{
public:
static FTexture *CreateTexture(const char *name);
virtual ~FTexture ();
virtual FImageSource *GetImage() const { return nullptr; }
const FString &GetName() const { return Name; }
bool isMasked() const { return bMasked; }
bool isTranslucent() const { return bMasked; }
PalEntry GetSkyCapColor(bool bottom);
// Returns the whole texture, stored in column-major order
virtual TArray<uint8_t> Get8BitPixels(bool alphatex);
virtual FBitmap GetBgraBitmap(PalEntry *remap, int *trans = nullptr);
static int SmoothEdges(unsigned char * buffer,int w, int h);
static PalEntry averageColor(const uint32_t *data, int size, int maxout);
int GetWidth() { return Width; }
int GetHeight() { return Height; }
int GetLeftOffset() { return LeftOffset; }
int GetTopOffset() { return TopOffset; }
FTextureBuffer CreateTexBuffer(int translation, int flags = 0);
bool GetTranslucency();
void CheckTrans(unsigned char * buffer, int size, int trans);
bool ProcessData(unsigned char * buffer, int w, int h, bool ispatch);
protected:
void CopySize(FTexture *BaseTexture)
{
Width = BaseTexture->GetWidth();
Height = BaseTexture->GetHeight();
TopOffset = BaseTexture->TopOffset;
TopOffset = BaseTexture->TopOffset;
}
int Width = 0, Height = 0;
int LeftOffset = 0, TopOffset = 0;
FString Name;
uint8_t bMasked = true; // Texture (might) have holes
int8_t bTranslucent = -1; // Does this texture have an active alpha channel?
bool skyColorDone = false;
PalEntry FloorSkyColor;
PalEntry CeilingSkyColor;
FTexture (const char *name = NULL);
};
extern TMap<FString, FTexture *> textures;
#endif

View file

@ -0,0 +1,52 @@
/*
** m_crc32.h
** Simple interface to zlib's CRC table
**
**---------------------------------------------------------------------------
** Copyright 1998-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 <zlib.h>
#include <stdint.h>
// zlib includes some CRC32 stuff, so just use that
inline const uint32_t *GetCRCTable () { return (const uint32_t *)get_crc_table(); }
inline uint32_t CalcCRC32 (const uint8_t *buf, unsigned int len)
{
return crc32 (0, buf, len);
}
inline uint32_t AddCRC32 (uint32_t crc, const uint8_t *buf, unsigned int len)
{
return crc32 (crc, buf, len);
}
inline uint32_t CRC1 (uint32_t crc, const uint8_t c, const uint32_t *crcTable)
{
return crcTable[(crc & 0xff) ^ c] ^ (crc >> 8);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,133 @@
#ifndef __M_PNG_H
#define __M_PNG_H
/*
** m_png.h
**
**---------------------------------------------------------------------------
** Copyright 2002-2005 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 <stdio.h>
#include "zstring.h"
#include "files.h"
#include "palentry.h"
// Screenshot buffer image data types
enum ESSType
{
SS_PAL,
SS_RGB,
SS_BGRA
};
class FileWriter;
// PNG Writing --------------------------------------------------------------
// Start writing an 8-bit palettized PNG file.
// The passed file should be a newly created file.
// This function writes the PNG signature and the IHDR, gAMA, PLTE, and IDAT
// chunks.
bool M_CreatePNG (FileWriter *file, const uint8_t *buffer, const PalEntry *pal,
ESSType color_type, int width, int height, int pitch, float gamma);
// Creates a grayscale 1x1 PNG file. Used for savegames without savepics.
bool M_CreateDummyPNG (FileWriter *file);
// Appends any chunk to a PNG file started with M_CreatePNG.
bool M_AppendPNGChunk (FileWriter *file, uint32_t chunkID, const uint8_t *chunkData, uint32_t len);
// Adds a tEXt chunk to a PNG file started with M_CreatePNG.
bool M_AppendPNGText (FileWriter *file, const char *keyword, const char *text);
// Appends the IEND chunk to a PNG file.
bool M_FinishPNG (FileWriter *file);
bool M_SaveBitmap(const uint8_t *from, ESSType color_type, int width, int height, int pitch, FileWriter *file);
// PNG Reading --------------------------------------------------------------
struct PNGHandle
{
struct Chunk
{
uint32_t ID;
uint32_t Offset;
uint32_t Size;
};
FileReader File;
bool bDeleteFilePtr;
TArray<Chunk> Chunks;
TArray<char *> TextChunks;
unsigned int ChunkPt;
PNGHandle(FileReader &file);
~PNGHandle();
};
// Verify that a file really is a PNG file. This includes not only checking
// the signature, but also checking for the IEND chunk. CRC checking of
// each chunk is not done. If it is valid, you get a PNGHandle to pass to
// the following functions.
PNGHandle *M_VerifyPNG (FileReader &file);
// Finds a chunk in a PNG file. The file pointer will be positioned at the
// beginning of the chunk data, and its length will be returned. A return
// value of 0 indicates the chunk was either not present or had 0 length.
unsigned int M_FindPNGChunk (PNGHandle *png, uint32_t chunkID);
// Finds a chunk in the PNG file, starting its search at whatever chunk
// the file pointer is currently positioned at.
unsigned int M_NextPNGChunk (PNGHandle *png, uint32_t chunkID);
// Finds a PNG text chunk with the given signature and returns a pointer
// to a NULL-terminated string if present. Returns NULL on failure.
// (Note: tEXt, not zTXt.)
char *M_GetPNGText (PNGHandle *png, const char *keyword);
bool M_GetPNGText (PNGHandle *png, const char *keyword, char *buffer, size_t buffsize);
// The file must be positioned at the start of the first IDAT. It reads
// image data into the provided buffer. Returns true on success.
bool M_ReadIDAT (FileReader &file, uint8_t *buffer, int width, int height, int pitch,
uint8_t bitdepth, uint8_t colortype, uint8_t interlace, unsigned int idatlen);
class FTexture;
FTexture *PNGTexture_CreateFromFile(PNGHandle *png, const FString &filename);
#ifndef MAKE_ID
#ifndef __BIG_ENDIAN__
#define MAKE_ID(a,b,c,d) ((uint32_t)((a)|((b)<<8)|((c)<<16)|((d)<<24)))
#else
#define MAKE_ID(a,b,c,d) ((uint32_t)((d)|((c)<<8)|((b)<<16)|((a)<<24)))
#endif
#endif
#endif

View file

@ -0,0 +1,417 @@
/*
** memarena.cpp
** Implements memory arenas.
**
**---------------------------------------------------------------------------
** Copyright 2010 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.
**---------------------------------------------------------------------------
**
** A memory arena is used for efficient allocation of many small objects that
** will all be freed at once. Note that since individual destructors are not
** called, you must not use an arena to allocate any objects that use a
** destructor, either explicitly or implicitly (because they have members
** with destructors).
*/
#include "basics.h"
#include "memarena.h"
#include "printf.h"
struct FMemArena::Block
{
Block *NextBlock;
void *Limit; // End of this block
void *Avail; // Start of free space in this block
void *alignme; // align to 16 bytes.
void Reset();
void *Alloc(size_t size);
};
//==========================================================================
//
// RoundPointer
//
// Rounds a pointer up to a pointer-sized boundary.
//
//==========================================================================
static inline void *RoundPointer(void *ptr)
{
return (void *)(((size_t)ptr + sizeof(void*) - 1) & ~(sizeof(void*) - 1));
}
//==========================================================================
//
// FMemArena Constructor
//
//==========================================================================
FMemArena::FMemArena(size_t blocksize)
{
TopBlock = NULL;
FreeBlocks = NULL;
BlockSize = blocksize;
}
//==========================================================================
//
// FMemArena Destructor
//
//==========================================================================
FMemArena::~FMemArena()
{
FreeAllBlocks();
}
//==========================================================================
//
// FMemArena :: Alloc
//
//==========================================================================
void *FMemArena::iAlloc(size_t size)
{
Block *block;
for (block = TopBlock; block != NULL; block = block->NextBlock)
{
void *res = block->Alloc(size);
if (res != NULL)
{
return res;
}
}
block = AddBlock(size);
return block->Alloc(size);
}
void *FMemArena::Alloc(size_t size)
{
return iAlloc((size + 15) & ~15);
}
//==========================================================================
//
// FMemArena :: FreeAll
//
// Moves all blocks to the free list. No system-level deallocation occurs.
//
//==========================================================================
void FMemArena::FreeAll()
{
for (Block *next, *block = TopBlock; block != NULL; block = next)
{
next = block->NextBlock;
block->Reset();
block->NextBlock = FreeBlocks;
FreeBlocks = block;
}
TopBlock = NULL;
}
//==========================================================================
//
// FMemArena :: FreeAllBlocks
//
// Frees all blocks used by this arena.
//
//==========================================================================
void FMemArena::FreeAllBlocks()
{
FreeBlockChain(TopBlock);
FreeBlockChain(FreeBlocks);
}
//==========================================================================
//
// FMemArena :: DumpInfo
//
// Prints some info about this arena
//
//==========================================================================
void FMemArena::DumpInfo()
{
size_t allocated = 0;
size_t used = 0;
for (auto block = TopBlock; block != NULL; block = block->NextBlock)
{
allocated += BlockSize;
used += BlockSize - ((char*)block->Limit - (char*)block->Avail);
}
Printf("%zu bytes allocated, %zu bytes in use\n", allocated, used);
}
//==========================================================================
//
// FMemArena :: DumpInfo
//
// Dumps the arena to a file (for debugging)
//
//==========================================================================
void FMemArena::DumpData(FILE *f)
{
for (auto block = TopBlock; block != NULL; block = block->NextBlock)
{
auto used = BlockSize - ((char*)block->Limit - (char*)block->Avail);
fwrite(block, 1, used, f);
}
}
//==========================================================================
//
// FMemArena :: FreeBlockChain
//
// Frees a chain of blocks.
//
//==========================================================================
void FMemArena::FreeBlockChain(Block *&top)
{
for (Block *next, *block = top; block != NULL; block = next)
{
next = block->NextBlock;
free(block);
}
top = NULL;
}
//==========================================================================
//
// FMemArena :: AddBlock
//
// Allocates a block large enough to hold at least <size> bytes and adds it
// to the TopBlock chain.
//
//==========================================================================
FMemArena::Block *FMemArena::AddBlock(size_t size)
{
Block *mem, **last;
size += sizeof(Block); // Account for header size
// Search for a free block to use
for (last = &FreeBlocks, mem = FreeBlocks; mem != NULL; last = &mem->NextBlock, mem = mem->NextBlock)
{
if ((uint8_t *)mem->Limit - (uint8_t *)mem >= (ptrdiff_t)size)
{
*last = mem->NextBlock;
break;
}
}
if (mem == NULL)
{
// Allocate a new block
if (size < BlockSize)
{
size = BlockSize;
}
else
{ // Stick some free space at the end so we can use this block for
// other things.
size += BlockSize/2;
}
mem = (Block *)malloc(size);
mem->Limit = (uint8_t *)mem + size;
}
mem->Reset();
mem->NextBlock = TopBlock;
TopBlock = mem;
return mem;
}
//==========================================================================
//
// FMemArena :: Block :: Reset
//
// Resets this block's Avail pointer.
//
//==========================================================================
void FMemArena::Block::Reset()
{
Avail = RoundPointer(reinterpret_cast<char*>(this) + sizeof(*this));
}
//==========================================================================
//
// FMemArena :: Block :: Alloc
//
// Allocates memory from the block if it has space. Returns NULL if not.
//
//==========================================================================
void *FMemArena::Block::Alloc(size_t size)
{
if ((char *)Avail + size > Limit)
{
return NULL;
}
void *res = Avail;
Avail = RoundPointer((char *)Avail + size);
return res;
}
//==========================================================================
//
// FSharedStringArena Constructor
//
//==========================================================================
FSharedStringArena::FSharedStringArena()
{
memset(Buckets, 0, sizeof(Buckets));
}
//==========================================================================
//
// FSharedStringArena Destructor
//
//==========================================================================
FSharedStringArena::~FSharedStringArena()
{
FreeAll();
// FMemArena destructor will free the blocks.
}
//==========================================================================
//
// FSharedStringArena :: Alloc
//
// Allocates a new string and initializes it with the passed string. This
// version takes an FString as a parameter, so it won't need to allocate any
// memory for the string text if it already exists in the arena.
//
//==========================================================================
FString *FSharedStringArena::Alloc(const FString &source)
{
unsigned int hash;
Node *strnode;
strnode = FindString(source, source.Len(), hash);
if (strnode == NULL)
{
strnode = (Node *)iAlloc(sizeof(Node));
::new(&strnode->String) FString(source);
strnode->Hash = hash;
hash %= Bucket_Count;
strnode->Next = Buckets[hash];
Buckets[hash] = strnode;
}
return &strnode->String;
}
//==========================================================================
//
// FSharedStringArena :: Alloc
//
//==========================================================================
FString *FSharedStringArena::Alloc(const char *source)
{
return Alloc(source, strlen(source));
}
//==========================================================================
//
// FSharedStringArena :: Alloc
//
//==========================================================================
FString *FSharedStringArena::Alloc(const char *source, size_t strlen)
{
unsigned int hash;
Node *strnode;
strnode = FindString(source, strlen, hash);
if (strnode == NULL)
{
strnode = (Node *)iAlloc(sizeof(Node));
::new(&strnode->String) FString(source, strlen);
strnode->Hash = hash;
hash %= Bucket_Count;
strnode->Next = Buckets[hash];
Buckets[hash] = strnode;
}
return &strnode->String;
}
//==========================================================================
//
// FSharedStringArena :: FindString
//
// Finds the string if it's already in the arena. Returns NULL if not.
//
//==========================================================================
FSharedStringArena::Node *FSharedStringArena::FindString(const char *str, size_t strlen, unsigned int &hash)
{
hash = SuperFastHash(str, strlen);
for (Node *node = Buckets[hash % Bucket_Count]; node != NULL; node = node->Next)
{
if (node->Hash == hash && node->String.Len() == strlen && memcmp(&node->String[0], str, strlen) == 0)
{
return node;
}
}
return NULL;
}
//==========================================================================
//
// FSharedStringArena :: FreeAll
//
// In addition to moving all used blocks onto the free list, all FStrings
// they contain will have their destructors called.
//
//==========================================================================
void FSharedStringArena::FreeAll()
{
for (Block *next, *block = TopBlock; block != NULL; block = next)
{
next = block->NextBlock;
void *limit = block->Avail;
block->Reset();
for (Node *string = (Node *)block->Avail; string < limit; ++string)
{
string->~Node();
}
block->NextBlock = FreeBlocks;
FreeBlocks = block;
}
memset(Buckets, 0, sizeof(Buckets));
TopBlock = NULL;
}

View file

@ -0,0 +1,97 @@
/*
** memarena.h
**
**---------------------------------------------------------------------------
** Copyright 2010 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.
**---------------------------------------------------------------------------
**
*/
#ifndef __MEMARENA_H
#define __MEMARENA_H
#include "zstring.h"
// A general purpose arena.
class FMemArena
{
public:
FMemArena(size_t blocksize = 10*1024);
~FMemArena();
void *Alloc(size_t size);
void FreeAll();
void FreeAllBlocks();
void DumpInfo();
void DumpData(FILE *f);
protected:
struct Block;
Block *AddBlock(size_t size);
void FreeBlockChain(Block *&top);
void *iAlloc(size_t size);
Block *TopBlock;
Block *FreeBlocks;
size_t BlockSize;
};
// An arena specializing in storage of FStrings. It knows how to free them,
// but this means it also should never be used for allocating anything else.
// Identical strings all return the same pointer.
class FSharedStringArena : public FMemArena
{
public:
FSharedStringArena();
~FSharedStringArena();
void FreeAll();
class FString *Alloc(const FString &source);
class FString *Alloc(const char *source);
class FString *Alloc(const char *source, size_t strlen);
protected:
struct Node
{
Node *Next;
FString String;
unsigned int Hash;
};
enum
{
Bucket_Count = 256
};
Node *Buckets[Bucket_Count];
Node *FindString(const char *str, size_t strlen, unsigned int &hash);
private:
void *Alloc(size_t size) { return NULL; } // No access to FMemArena::Alloc for outsiders.
};
#endif

View file

@ -0,0 +1,82 @@
#pragma once
#include <stdint.h>
struct PalEntry
{
PalEntry() = default;
PalEntry (uint32_t argb) { d = argb; }
operator uint32_t () const { return d; }
void SetRGB(PalEntry other)
{
d = other.d & 0xffffff;
}
PalEntry Modulate(PalEntry other) const
{
if (isWhite())
{
return other;
}
else if (other.isWhite())
{
return *this;
}
else
{
other.r = (r * other.r) / 255;
other.g = (g * other.g) / 255;
other.b = (b * other.b) / 255;
return other;
}
}
int Luminance() const
{
return (r * 77 + g * 143 + b * 37) >> 8;
}
void Decolorize() // this for 'nocoloredspritelighting' and not the same as desaturation. The normal formula results in a value that's too dark.
{
int v = (r + g + b);
r = g = b = ((255*3) + v + v) / 9;
}
bool isBlack() const
{
return (d & 0xffffff) == 0;
}
bool isWhite() const
{
return (d & 0xffffff) == 0xffffff;
}
PalEntry &operator= (const PalEntry &other) = default;
PalEntry &operator= (uint32_t other) { d = other; return *this; }
PalEntry InverseColor() const { PalEntry nc; nc.a = a; nc.r = 255 - r; nc.g = 255 - g; nc.b = 255 - b; return nc; }
#ifdef __BIG_ENDIAN__
PalEntry (uint8_t ir, uint8_t ig, uint8_t ib) : a(0), r(ir), g(ig), b(ib) {}
PalEntry (uint8_t ia, uint8_t ir, uint8_t ig, uint8_t ib) : a(ia), r(ir), g(ig), b(ib) {}
union
{
struct
{
uint8_t a,r,g,b;
};
uint32_t d;
};
#else
PalEntry (uint8_t ir, uint8_t ig, uint8_t ib) : b(ib), g(ig), r(ir), a(0) {}
PalEntry (uint8_t ia, uint8_t ir, uint8_t ig, uint8_t ib) : b(ib), g(ig), r(ir), a(ia) {}
union
{
struct
{
uint8_t b,g,r,a;
};
uint32_t d;
};
#endif
};
inline int Luminance(int r, int g, int b)
{
return (r * 77 + g * 143 + b * 37) >> 8;
}

View file

@ -1,5 +1,6 @@
// Windows layer-independent code
#include <Windows.h>
#include "compat.h"
#include "build.h"
#include "baselayer.h"