- added back the paletted texture readers.

... after finding out what an inefficient and poorly working method the Build backend uses for downconverting true color textures.
This commit is contained in:
Christoph Oelckers 2019-10-11 19:21:36 +02:00
parent 14b21bab7a
commit fdbb27a796
16 changed files with 956 additions and 25 deletions

View file

@ -1055,6 +1055,7 @@ set (PCH_SOURCES
common/textures/texture.cpp common/textures/texture.cpp
common/textures/image.cpp common/textures/image.cpp
common/textures/imagetexture.cpp common/textures/imagetexture.cpp
common/textures/imagehelpers.cpp
common/textures/formats/buildtexture.cpp common/textures/formats/buildtexture.cpp
common/textures/formats/ddstexture.cpp common/textures/formats/ddstexture.cpp
common/textures/formats/jpegtexture.cpp common/textures/formats/jpegtexture.cpp
@ -1223,6 +1224,7 @@ install(TARGETS demolition
source_group("Utility" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/utility/.+") 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" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/textures/.+")
source_group("Code\\Textures\\Formats" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/textures/formats/.+") source_group("Code\\Textures\\Formats" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/textures/formats/.+")
source_group("Code\\Utility" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/utility/.+")
source_group("Utility\\Audiolib" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/audiolib/.+") 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 Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/audiolib/include/.+")
source_group("Utility\\Audiolib Sources" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/audiolib/src/.+") source_group("Utility\\Audiolib Sources" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/audiolib/src/.+")

View file

@ -52,6 +52,7 @@ class FArtTexture : public FImageSource
public: public:
FArtTexture (int width, int height, int p); FArtTexture (int width, int height, int p);
void CreatePalettedPixels(uint8_t* buffer);
int CopyPixels(FBitmap *bmp, int conversion) override; int CopyPixels(FBitmap *bmp, int conversion) override;
int32_t picanmdisk; // Todo: Get this out again on the other side. int32_t picanmdisk; // Todo: Get this out again on the other side.
}; };
@ -104,6 +105,20 @@ FArtTexture::FArtTexture(int width, int height, int p)
picanmdisk = p; picanmdisk = p;
} }
//==========================================================================
//
// This will never be called by the software renderer but let's be safe.
//
//==========================================================================
void FArtTexture::CreatePalettedPixels(uint8_t* buffer)
{
FileReader fr = kopenFileReader(Name, 0);
if (!fr.isOpen()) return;
int numpixels = Width * Height;
fr.Read(buffer, numpixels);
}
//=========================================================================== //===========================================================================
// //
// FArtTexture::CopyPixels // FArtTexture::CopyPixels

View file

@ -53,7 +53,7 @@ class FBuildTexture : public FImageSource
{ {
public: public:
FBuildTexture (const FString &pathprefix, int tilenum, const uint8_t *pixels, int width, int height, int left, int top); 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; //void CreatePalettedPixels(uint8_t *destbuffer) override;
protected: protected:
const uint8_t *RawPixels; const uint8_t *RawPixels;
@ -75,12 +75,14 @@ FBuildTexture::FBuildTexture(const FString &pathprefix, int tilenum, const uint8
TopOffset = top; TopOffset = top;
} }
#if 0
TArray<uint8_t> FBuildTexture::CreatePalettedPixels(int conversion) TArray<uint8_t> FBuildTexture::CreatePalettedPixels(int conversion)
{ {
TArray<uint8_t> Pixels(Width * Height, true); TArray<uint8_t> Pixels(Width * Height, true);
memcpy(Pixels.Data(), RawPixels, Width * Height); memcpy(Pixels.Data(), RawPixels, Width * Height);
return Pixels; return Pixels;
} }
#endif
#if 0 #if 0
//=========================================================================== //===========================================================================

View file

@ -51,6 +51,7 @@
#include <stdint.h> #include <stdint.h>
#include "files.h" #include "files.h"
#include "bitmap.h" #include "bitmap.h"
#include "imagehelpers.h"
#include "image.h" #include "image.h"
#include "m_png.h" #include "m_png.h"
#include "cache1d.h" #include "cache1d.h"
@ -163,6 +164,8 @@ class FDDSTexture : public FImageSource
public: public:
FDDSTexture (FileReader &lump, void *surfdesc); FDDSTexture (FileReader &lump, void *surfdesc);
void CreatePalettedPixels(uint8_t *destbuffer) override;
protected: protected:
uint32_t Format; uint32_t Format;
@ -362,6 +365,38 @@ void FDDSTexture::CalcBitShift (uint32_t mask, uint8_t *lshiftp, uint8_t *rshift
*rshiftp = shift; *rshiftp = shift;
} }
//==========================================================================
//
//
//
//==========================================================================
void FDDSTexture::CreatePalettedPixels(uint8_t *buffer)
{
auto lump = kopenFileReader(Name, 0);
if (!lump.isOpen()) return; // Just leave the texture blank.
lump.Seek (sizeof(DDSURFACEDESC2) + 4, FileReader::SeekSet);
int pmode = PIX_Palette;
if (Format >= 1 && Format <= 4) // RGB: Format is # of bytes per pixel
{
ReadRGB (lump, buffer, pmode);
}
else if (Format == ID_DXT1)
{
DecompressDXT1 (lump, buffer, pmode);
}
else if (Format == ID_DXT3 || Format == ID_DXT2)
{
DecompressDXT3 (lump, Format == ID_DXT2, buffer, pmode);
}
else if (Format == ID_DXT5 || Format == ID_DXT4)
{
DecompressDXT5 (lump, Format == ID_DXT4, buffer, pmode);
}
}
//========================================================================== //==========================================================================
// //
// Note that pixel size == 8 is column-major, but 32 is row-major! // Note that pixel size == 8 is column-major, but 32 is row-major!
@ -400,7 +435,20 @@ void FDDSTexture::ReadRGB (FileReader &lump, uint8_t *buffer, int pixelmode)
} }
if (pixelmode != PIX_ARGB) if (pixelmode != PIX_ARGB)
{ {
assert(false); if (amask == 0 || (c & amask))
{
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 = ImageHelpers::RGBToPalette(false, r >> 24, g >> 24, b >> 24, a >> 24);
}
else
{
*pixelp = 0;
bMasked = true;
}
pixelp += Height;
} }
else else
{ {
@ -479,7 +527,7 @@ void FDDSTexture::DecompressDXT1 (FileReader &lump, uint8_t *buffer, int pixelmo
// Pick colors from the palette for each of the four colors. // Pick colors from the palette for each of the four colors.
if (pixelmode != PIX_ARGB) for (i = 3; i >= 0; --i) if (pixelmode != PIX_ARGB) for (i = 3; i >= 0; --i)
{ {
assert(false); palcol[i] = ImageHelpers::RGBToPalette(pixelmode == PIX_Alphatex, color[i]);
} }
// Now decode this 4x4 block to the pixel buffer. // Now decode this 4x4 block to the pixel buffer.
for (y = 0; y < 4; ++y) for (y = 0; y < 4; ++y)
@ -559,7 +607,7 @@ void FDDSTexture::DecompressDXT3 (FileReader &lump, bool premultiplied, uint8_t
// Pick colors from the palette for each of the four colors. // Pick colors from the palette for each of the four colors.
if (pixelmode != PIX_ARGB) for (i = 3; i >= 0; --i) if (pixelmode != PIX_ARGB) for (i = 3; i >= 0; --i)
{ {
assert(false); palcol[i] = ImageHelpers::RGBToPalette(pixelmode == PIX_Alphatex, color[i], false);
} }
// Now decode this 4x4 block to the pixel buffer. // Now decode this 4x4 block to the pixel buffer.
@ -671,7 +719,7 @@ void FDDSTexture::DecompressDXT5 (FileReader &lump, bool premultiplied, uint8_t
// Pick colors from the palette for each of the four colors. // Pick colors from the palette for each of the four colors.
if (pixelmode != PIX_ARGB) for (i = 3; i >= 0; --i) if (pixelmode != PIX_ARGB) for (i = 3; i >= 0; --i)
{ {
assert(false); palcol[i] = ImageHelpers::RGBToPalette(pixelmode == PIX_Alphatex, color[i], false);
} }
// Now decode this 4x4 block to the pixel buffer. // Now decode this 4x4 block to the pixel buffer.
for (y = 0; y < 4; ++y) for (y = 0; y < 4; ++y)

View file

@ -40,6 +40,7 @@
#include "bitmap.h" #include "bitmap.h"
#include "image.h" #include "image.h"
#include "cache1d.h" #include "cache1d.h"
#include "imagehelpers.h"
extern "C" extern "C"
{ {
@ -183,6 +184,7 @@ class FJPEGTexture : public FImageSource
public: public:
FJPEGTexture (int width, int height); FJPEGTexture (int width, int height);
void CreatePalettedPixels(uint8_t* destbuffer) override;
int CopyPixels(FBitmap *bmp, int conversion) override; int CopyPixels(FBitmap *bmp, int conversion) override;
}; };
@ -250,6 +252,125 @@ FJPEGTexture::FJPEGTexture (int width, int height)
Height = height; Height = height;
} }
//==========================================================================
//
//
//
//==========================================================================
void FJPEGTexture::CreatePalettedPixels(uint8_t *buffer)
{
auto lump = kopenFileReader(Name, 0);
if (!lump.isOpen()) return; // Just leave the texture blank.
JSAMPLE *buff = NULL;
jpeg_decompress_struct cinfo;
jpeg_error_mgr jerr;
TArray<uint8_t> Pixels(Width * Height, true);
memset (Pixels.Data(), 0xBA, Width * Height);
cinfo.err = jpeg_std_error(&jerr);
cinfo.err->output_message = JPEG_OutputMessage;
cinfo.err->error_exit = JPEG_ErrorExit;
jpeg_create_decompress(&cinfo);
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 y = 0;
buff = new uint8_t[cinfo.output_width * cinfo.output_components];
while (cinfo.output_scanline < cinfo.output_height)
{
int num_scanlines = jpeg_read_scanlines(&cinfo, &buff, 1);
uint8_t *in = buff;
uint8_t *out = Pixels.Data() + y;
switch (cinfo.out_color_space)
{
case JCS_RGB:
for (int x = Width; x > 0; --x)
{
*out = ImageHelpers::RGBToPalette(false, in[0], in[1], in[2]);
out += Height;
in += 3;
}
break;
case JCS_GRAYSCALE:
{
auto remap = ImageHelpers::GetGraymap();
for (int x = Width; x > 0; --x)
{
*out = remap[in[0]];
out += Height;
in += 1;
}
break;
}
case JCS_CMYK:
// What are you doing using a CMYK image? :)
for (int x = Width; x > 0; --x)
{
// To be precise, these calculations should use 255, but
// 256 is much faster and virtually indistinguishable.
int r = in[3] - (((256 - in[0])*in[3]) >> 8);
int g = in[3] - (((256 - in[1])*in[3]) >> 8);
int b = in[3] - (((256 - in[2])*in[3]) >> 8);
*out = ImageHelpers::RGBToPalette(false, r, g, b);
out += Height;
in += 4;
}
break;
case JCS_YCbCr:
// Probably useless but since I had the formula available...
for (int x = Width; x > 0; --x)
{
double Y = in[0], Cb = in[1], Cr = in[2];
int r = clamp((int)(Y + 1.40200 * (Cr - 0x80)), 0, 255);
int g = clamp((int)(Y - 0.34414 * (Cb - 0x80) - 0.71414 * (Cr - 0x80)), 0, 255);
int b = clamp((int)(Y + 1.77200 * (Cb - 0x80)), 0, 255);
*out = ImageHelpers::RGBToPalette(false, r, g, b);
out += Height;
in += 4;
}
break;
default:
// The other colorspaces were considered above and discarded,
// but GCC will complain without a default for them here.
break;
}
y++;
}
jpeg_finish_decompress(&cinfo);
}
}
catch (int)
{
Printf(TEXTCOLOR_ORANGE "JPEG error in %s\n", Name.GetChars());
}
jpeg_destroy_decompress(&cinfo);
if (buff != NULL)
{
delete[] buff;
}
}
//=========================================================================== //===========================================================================
// //
// FJPEGTexture::CopyPixels // FJPEGTexture::CopyPixels

View file

@ -37,6 +37,7 @@
#include "basics.h" #include "basics.h"
#include "files.h" #include "files.h"
#include "bitmap.h" #include "bitmap.h"
#include "imagehelpers.h"
#include "image.h" #include "image.h"
#include "cache1d.h" #include "cache1d.h"
@ -90,6 +91,8 @@ protected:
void ReadPCX4bits (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 ReadPCX8bits (uint8_t *dst, FileReader & lump, PCXHeader *hdr);
void ReadPCX24bits (uint8_t *dst, FileReader & lump, PCXHeader *hdr, int planes); void ReadPCX24bits (uint8_t *dst, FileReader & lump, PCXHeader *hdr, int planes);
void CreatePalettedPixels(uint8_t *destbuffer) override;
}; };
@ -335,6 +338,89 @@ void FPCXTexture::ReadPCX24bits (uint8_t *dst, FileReader & lump, PCXHeader *hdr
} }
} }
//==========================================================================
//
//
//
//==========================================================================
void FPCXTexture::CreatePalettedPixels(uint8_t *buffer)
{
uint8_t PaletteMap[256];
PCXHeader header;
int bitcount;
auto lump = kopenFileReader(Name, 0);
if (!lump.isOpen()) return; // Just leave the texture blank.
lump.Read(&header, sizeof(header));
bitcount = header.bitsPerPixel * header.numColorPlanes;
if (bitcount < 24)
{
if (bitcount < 8)
{
switch (bitcount)
{
default:
case 1:
PaletteMap[0] = ImageHelpers::GrayMap[0];
PaletteMap[1] = ImageHelpers::GrayMap[255];
ReadPCX1bit (buffer, lump, &header);
break;
case 4:
for (int i = 0; i < 16; i++)
{
PaletteMap[i] = ImageHelpers::RGBToPalettePrecise(false, header.palette[i * 3], header.palette[i * 3 + 1], header.palette[i * 3 + 2]);
}
ReadPCX4bits (buffer, lump, &header);
break;
}
}
else if (bitcount == 8)
{
lump.Seek(-769, FileReader::SeekEnd);
uint8_t c = lump.ReadUInt8();
//if (c !=0x0c) memcpy(PaletteMap, GrayMap, 256); // Fallback for files without palette
//else
for(int i=0;i<256;i++)
{
uint8_t r = lump.ReadUInt8();
uint8_t g = lump.ReadUInt8();
uint8_t b = lump.ReadUInt8();
PaletteMap[i] = ImageHelpers::RGBToPalettePrecise(false, r, g, b);
}
lump.Seek(sizeof(header), FileReader::SeekSet);
ReadPCX8bits (buffer, lump, &header);
}
if (Width == Height)
{
ImageHelpers::FlipSquareBlockRemap(buffer, Width, PaletteMap);
}
else
{
TArray<uint8_t> newpix(Width*Height, true);
ImageHelpers::FlipNonSquareBlockRemap (newpix.Data(), buffer, Width, Height, Width, PaletteMap);
}
}
else
{
TArray<uint8_t> buffer(Width*Height * 3, true);
uint8_t * row = buffer.Data();
ReadPCX24bits (row, lump, &header, 3);
for(int y=0; y<Height; y++)
{
for(int x=0; x < Width; x++)
{
buffer[y + Height * x] = ImageHelpers::RGBToPalette(false, row[0], row[1], row[2]);
row+=3;
}
}
}
}
//=========================================================================== //===========================================================================
// //
// FPCXTexture::CopyPixels // FPCXTexture::CopyPixels

View file

@ -37,6 +37,7 @@
#include "templates.h" #include "templates.h"
#include "m_png.h" #include "m_png.h"
#include "bitmap.h" #include "bitmap.h"
#include "imagehelpers.h"
#include "image.h" #include "image.h"
#include "printf.h" #include "printf.h"
#include "cache1d.h" #include "cache1d.h"
@ -52,6 +53,7 @@ class FPNGTexture : public FImageSource
public: public:
FPNGTexture (FileReader &lump, int width, int height, uint8_t bitdepth, uint8_t colortype, uint8_t interlace); FPNGTexture (FileReader &lump, int width, int height, uint8_t bitdepth, uint8_t colortype, uint8_t interlace);
void CreatePalettedPixels(uint8_t* buffer);
int CopyPixels(FBitmap *bmp, int conversion) override; int CopyPixels(FBitmap *bmp, int conversion) override;
protected: protected:
@ -61,7 +63,8 @@ protected:
bool HaveTrans; bool HaveTrans;
uint16_t NonPaletteTrans[3]; uint16_t NonPaletteTrans[3];
int PaletteSize = 0; uint8_t PaletteMap[256];
uint32_t PaletteSize = 0;
uint32_t StartOfIDAT = 0; uint32_t StartOfIDAT = 0;
uint32_t StartOfPalette = 0; uint32_t StartOfPalette = 0;
}; };
@ -145,8 +148,14 @@ FPNGTexture::FPNGTexture (FileReader &lump, int width, int height,
uint8_t depth, uint8_t colortype, uint8_t interlace) uint8_t depth, uint8_t colortype, uint8_t interlace)
: BitDepth(depth), ColorType(colortype), Interlace(interlace), HaveTrans(false) : BitDepth(depth), ColorType(colortype), Interlace(interlace), HaveTrans(false)
{ {
union
{
PalEntry palette[256];
uint8_t pngpal[256][3];
} p;
uint8_t trans[256]; uint8_t trans[256];
uint32_t len, id; uint32_t len, id;
int i;
bMasked = false; bMasked = false;
@ -197,6 +206,15 @@ FPNGTexture::FPNGTexture (FileReader &lump, int width, int height,
case MAKE_ID('P','L','T','E'): case MAKE_ID('P','L','T','E'):
PaletteSize = std::min<int> (len / 3, 256); PaletteSize = std::min<int> (len / 3, 256);
StartOfPalette = (uint32_t)lump.Tell(); StartOfPalette = (uint32_t)lump.Tell();
lump.Read (p.pngpal, PaletteSize * 3);
if (PaletteSize * 3 != (int)len)
{
lump.Seek (len - PaletteSize * 3, FileReader::SeekCur);
}
for (i = PaletteSize - 1; i >= 0; --i)
{
p.palette[i] = PalEntry(p.pngpal[i][0], p.pngpal[i][1], p.pngpal[i][2]);
}
break; break;
case MAKE_ID('t','R','N','S'): case MAKE_ID('t','R','N','S'):
@ -219,16 +237,36 @@ FPNGTexture::FPNGTexture (FileReader &lump, int width, int height,
{ {
case 4: // Grayscale + Alpha case 4: // Grayscale + Alpha
bMasked = true; bMasked = true;
break; // intentional fall-through
case 0: // Grayscale case 0: // Grayscale
if (colortype == 0 && HaveTrans && NonPaletteTrans[0] < 256) if (colortype == 0 && HaveTrans && NonPaletteTrans[0] < 256)
{ {
bMasked = true; bMasked = true;
PaletteSize = 256;
memcpy (PaletteMap, ImageHelpers::GrayMap+1, 256);
PaletteMap[255] = 254; // cannot use 255.
PaletteMap[NonPaletteTrans[0]] = 255;
}
else
{
memcpy(PaletteMap, ImageHelpers::GrayMap, 256);
} }
break; break;
case 3: // Paletted case 3: // Paletted
for (i = 0; i < PaletteSize; ++i)
{
if (trans[i] == 0)
{
bMasked = true;
PaletteMap[i] = 255;
}
else
{
PaletteMap[i] = ImageHelpers::BestColor(p.palette[i].r, p.palette[i].g, p.palette[i].b);
}
}
break; break;
case 6: // RGB + Alpha case 6: // RGB + Alpha
@ -241,6 +279,116 @@ FPNGTexture::FPNGTexture (FileReader &lump, int width, int height,
} }
} }
//==========================================================================
//
//
//
//==========================================================================
void FPNGTexture::CreatePalettedPixels(uint8_t *buffer)
{
FileReader *lump;
FileReader lfr;
lfr = kopenFileReader(Name, 0);
if (!lfr.isOpen()) return;
lump = &lfr;
TArray<uint8_t> Pixels(Width*Height, true);
if (StartOfIDAT == 0)
{
memset (Pixels.Data(), 0x99, Width*Height);
}
else
{
uint32_t len, id;
lump->Seek (StartOfIDAT, FileReader::SeekSet);
lump->Read(&len, 4);
lump->Read(&id, 4);
if (ColorType == 0 || ColorType == 3) /* Grayscale and paletted */
{
M_ReadIDAT (*lump, Pixels.Data(), Width, Height, Width, BitDepth, ColorType, Interlace, BigLong((unsigned int)len));
if (Width == Height)
{
ImageHelpers::FlipSquareBlockRemap (Pixels.Data(), Width, PaletteMap);
}
else
{
TArray<uint8_t> newpix(Width*Height, true);
ImageHelpers::FlipNonSquareBlockRemap (newpix.Data(), Pixels.Data(), Width, Height, Width, PaletteMap);
}
}
else /* RGB and/or Alpha present */
{
int bytesPerPixel = ColorType == 2 ? 3 : ColorType == 4 ? 2 : 4;
uint8_t *tempix = new uint8_t[Width * Height * bytesPerPixel];
uint8_t *in, *out;
int x, y, pitch, backstep;
M_ReadIDAT (*lump, tempix, Width, Height, Width*bytesPerPixel, BitDepth, ColorType, Interlace, BigLong((unsigned int)len));
in = tempix;
out = Pixels.Data();
// Convert from source format to paletted, column-major.
// Formats with alpha maps are reduced to only 1 bit of alpha.
switch (ColorType)
{
case 2: // RGB
pitch = Width * 3;
backstep = Height * pitch - 3;
for (x = Width; x > 0; --x)
{
for (y = Height; y > 0; --y)
{
if (HaveTrans && in[0] == NonPaletteTrans[0] && in[1] == NonPaletteTrans[1] && in[2] == NonPaletteTrans[2])
{
*out++ = 255;
}
else
{
*out++ = ImageHelpers::RGBToPalette(false, in[0], in[1], in[2]);
}
in += pitch;
}
in -= backstep;
}
break;
case 4: // Grayscale + Alpha
pitch = Width * 2;
backstep = Height * pitch - 2;
for (x = Width; x > 0; --x)
{
for (y = Height; y > 0; --y)
{
*out++ = PaletteMap[in[0]];
in += pitch;
}
in -= backstep;
}
break;
case 6: // RGB + Alpha
pitch = Width * 4;
backstep = Height * pitch - 4;
for (x = Width; x > 0; --x)
{
for (y = Height; y > 0; --y)
{
*out++ = ImageHelpers::RGBToPalette(false, in[0], in[1], in[2], in[3]);
in += pitch;
}
in -= backstep;
}
break;
}
delete[] tempix;
}
}
}
//=========================================================================== //===========================================================================
// //
// FPNGTexture::CopyPixels // FPNGTexture::CopyPixels

View file

@ -47,6 +47,7 @@
#include "files.h" #include "files.h"
#include "bitmap.h" #include "bitmap.h"
#include "imagehelpers.h"
#include "image.h" #include "image.h"
#include "cache1d.h" #include "cache1d.h"
@ -66,6 +67,7 @@ class FStbTexture : public FImageSource
public: public:
FStbTexture (int w, int h); FStbTexture (int w, int h);
void CreatePalettedPixels(uint8_t *destbuffer) override;
int CopyPixels(FBitmap *bmp, int conversion) override; int CopyPixels(FBitmap *bmp, int conversion) override;
}; };
@ -114,9 +116,46 @@ FStbTexture::FStbTexture (int w, int h)
// //
//========================================================================== //==========================================================================
void FStbTexture::CreatePalettedPixels(uint8_t *buffer)
{
FBitmap bitmap;
bitmap.Create(Width, Height);
CopyPixels(&bitmap, 0);
const uint8_t *data = bitmap.GetPixels();
uint8_t *dest_p;
int dest_adv = Height;
int dest_rew = Width * Height - 1;
dest_p = buffer;
// Convert the source image from row-major to column-major format and remap it
for (int y = Height; y != 0; --y)
{
for (int x = Width; x != 0; --x)
{
int b = *data++;
int g = *data++;
int r = *data++;
int a = *data++;
if (a < 128) *dest_p = 0;
else *dest_p = ImageHelpers::RGBToPalette(false, r, g, b);
dest_p += dest_adv;
}
dest_p -= dest_rew;
}
}
//==========================================================================
//
//
//
//==========================================================================
int FStbTexture::CopyPixels(FBitmap *bmp, int conversion) int FStbTexture::CopyPixels(FBitmap *bmp, int conversion)
{ {
auto lump = kopenFileReader(Name, 0); auto lump = kopenFileReader(Name, 0);
if (!lump.isOpen()) return -1; // Just leave the texture blank.
int x, y, chan; int x, y, chan;
auto image = stbi_load_from_callbacks(&callbacks, &lump, &x, &y, &chan, STBI_rgb_alpha); auto image = stbi_load_from_callbacks(&callbacks, &lump, &x, &y, &chan, STBI_rgb_alpha);
if (image) if (image)

View file

@ -38,6 +38,7 @@
#include "bitmap.h" #include "bitmap.h"
#include "image.h" #include "image.h"
#include "cache1d.h" #include "cache1d.h"
#include "imagehelpers.h"
//========================================================================== //==========================================================================
@ -82,6 +83,7 @@ public:
protected: protected:
void ReadCompressed(FileReader &lump, uint8_t * buffer, int bytesperpixel); void ReadCompressed(FileReader &lump, uint8_t * buffer, int bytesperpixel);
void CreatePalettedPixels(uint8_t *destbuffer) override;
}; };
//========================================================================== //==========================================================================
@ -168,6 +170,212 @@ void FTGATexture::ReadCompressed(FileReader &lump, uint8_t * buffer, int bytespe
} }
} }
//==========================================================================
//
//
//
//==========================================================================
void FTGATexture::CreatePalettedPixels(uint8_t *buffer)
{
uint8_t PaletteMap[256];
auto lump = kopenFileReader(Name, 0);
if (!lump.isOpen()) return;
TGAHeader hdr;
uint16_t w;
uint8_t r,g,b,a;
TArray<uint8_t> Pixels(Width*Height, true);
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(PaletteMap, 0, 256);
for (int i = hdr.cm_first; i < hdr.cm_first + hdr.cm_length && i < 256; i++)
{
switch (hdr.cm_size)
{
case 15:
case 16:
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;
break;
default: // should never happen
r=g=b=a=0;
break;
}
PaletteMap[i] = ImageHelpers::RGBToPalettePrecise(false, r, g, b, a);
}
}
int Size = Width * Height * (hdr.bpp>>3);
if (hdr.img_type < 4) // uncompressed
{
lump.Read(buffer, Size);
}
else // compressed
{
ReadCompressed(lump, buffer, hdr.bpp>>3);
}
uint8_t * ptr = buffer;
int step_x = (hdr.bpp>>3);
int Pitch = Width * step_x;
/*
if (hdr.img_desc&32)
{
ptr += (Width-1) * step_x;
step_x =- step_x;
}
*/
if (!(hdr.img_desc&32))
{
ptr += (Height-1) * Pitch;
Pitch = -Pitch;
}
switch (hdr.img_type & 7)
{
case 1: // paletted
for(int y=0;y<Height;y++)
{
uint8_t * p = ptr + y * Pitch;
for(int x=0;x<Width;x++)
{
Pixels[x*Height+y] = PaletteMap[*p];
p+=step_x;
}
}
break;
case 2: // RGB
switch (hdr.bpp)
{
case 15:
case 16:
step_x>>=1;
for(int y=0;y<Height;y++)
{
uint16_t * p = (uint16_t*)(ptr + y * Pitch);
for(int x=0;x<Width;x++)
{
int v = LittleShort(*p);
Pixels[x*Height + y] = ImageHelpers::RGBToPalette(false, ((v >> 10) & 0x1f) * 8, ((v >> 5) & 0x1f) * 8, (v & 0x1f) * 8);
p+=step_x;
}
}
break;
case 24:
for(int y=0;y<Height;y++)
{
uint8_t * p = ptr + y * Pitch;
for(int x=0;x<Width;x++)
{
Pixels[x*Height + y] = ImageHelpers::RGBToPalette(false, p[2], p[1], p[0]);
p+=step_x;
}
}
break;
case 32:
if ((hdr.img_desc&15)!=8) // 32 bits without a valid alpha channel
{
for(int y=0;y<Height;y++)
{
uint8_t * p = ptr + y * Pitch;
for(int x=0;x<Width;x++)
{
Pixels[x*Height + y] = ImageHelpers::RGBToPalette(false, p[2], p[1], p[0]);
p+=step_x;
}
}
}
else
{
for(int y=0;y<Height;y++)
{
uint8_t * p = ptr + y * Pitch;
for(int x=0;x<Width;x++)
{
Pixels[x*Height + y] = ImageHelpers::RGBToPalette(false, p[2], p[1], p[0], p[3]);
p+=step_x;
}
}
}
break;
default:
break;
}
break;
case 3: // Grayscale
{
auto remap = ImageHelpers::GetGraymap();
switch (hdr.bpp)
{
case 8:
for (int y = 0; y < Height; y++)
{
uint8_t * p = ptr + y * Pitch;
for (int x = 0; x < Width; x++)
{
Pixels[x*Height + y] = remap[*p];
p += step_x;
}
}
break;
case 16:
for (int y = 0; y < Height; y++)
{
uint8_t * p = ptr + y * Pitch;
for (int x = 0; x < Width; x++)
{
Pixels[x*Height + y] = remap[p[1]]; // only use the high byte
p += step_x;
}
}
break;
default:
break;
}
break;
}
default:
break;
}
}
//=========================================================================== //===========================================================================
// //
// FTGATexture::CopyPixels // FTGATexture::CopyPixels
@ -178,6 +386,7 @@ int FTGATexture::CopyPixels(FBitmap *bmp, int conversion)
{ {
PalEntry pe[256]; PalEntry pe[256];
auto lump = kopenFileReader(Name, 0); auto lump = kopenFileReader(Name, 0);
if (!lump.isOpen()) return -1;
TGAHeader hdr; TGAHeader hdr;
uint16_t w; uint16_t w;
uint8_t r,g,b,a; uint8_t r,g,b,a;
@ -232,7 +441,7 @@ int FTGATexture::CopyPixels(FBitmap *bmp, int conversion)
} }
int Size = Width * Height * (hdr.bpp>>3); int Size = Width * Height * (hdr.bpp>>3);
TArray<uint8_t> sbuffer(Size); TArray<uint8_t> sbuffer(Size, true);
if (hdr.img_type < 4) // uncompressed if (hdr.img_type < 4) // uncompressed
{ {

View file

@ -39,6 +39,7 @@
#include "image.h" #include "image.h"
#include "files.h" #include "files.h"
#include "cache1d.h" #include "cache1d.h"
#include "imagehelpers.h"
int FImageSource::NextID; int FImageSource::NextID;
@ -48,13 +49,15 @@ int FImageSource::NextID;
// //
//=========================================================================== //===========================================================================
TArray<uint8_t> FImageSource::CreatePalettedPixels(int conversion) void FImageSource::CreatePalettedPixels(uint8_t *buffer)
{ {
TArray<uint8_t> Pixels(Width * Height, true); memset(buffer, 0, Width * Height);
memset(Pixels.Data(), 0, Width * Height);
return Pixels;
} }
const uint8_t* FImageSource::GetPalettedPixels()
{
return nullptr;
}
//=========================================================================== //===========================================================================
// //
@ -70,14 +73,21 @@ TArray<uint8_t> FImageSource::CreatePalettedPixels(int conversion)
int FImageSource::CopyTranslatedPixels(FBitmap *bmp, PalEntry *remap) int FImageSource::CopyTranslatedPixels(FBitmap *bmp, PalEntry *remap)
{ {
auto ppix = CreatePalettedPixels(false); TArray<uint8_t> buffer;
bmp->CopyPixelData(0, 0, ppix.Data(), Width, Height, Height, 1, 0, remap); const uint8_t* ppix = GetPalettedPixels();
if (ppix == nullptr)
{
buffer.Resize(Width * Height);
CreatePalettedPixels(buffer.Data());
ppix = buffer.Data();
}
bmp->CopyPixelData(0, 0, ppix, Width, Height, Height, 1, 0, remap);
return 0; return 0;
} }
int FImageSource::CopyPixels(FBitmap* bmp, int conversion) int FImageSource::CopyPixels(FBitmap* bmp, int conversion)
{ {
return CopyTranslatedPixels(bmp, nullptr); // This should never get called for ART tiles. return CopyTranslatedPixels(bmp, ImageHelpers::BaseColors); // This should never get called for ART tiles.
} }

View file

@ -61,7 +61,8 @@ public:
// 'noremap0' will only be looked at by FPatchTexture and forwarded by FMultipatchTexture. // 'noremap0' will only be looked at by FPatchTexture and forwarded by FMultipatchTexture.
static FImageSource * GetImage(const char *name); static FImageSource * GetImage(const char *name);
virtual TArray<uint8_t> CreatePalettedPixels(int conversion); virtual void CreatePalettedPixels(uint8_t *destbuffer);
virtual const uint8_t* GetPalettedPixels();
virtual int CopyPixels(FBitmap* bmp, int conversion); // This will always ignore 'luminance'. virtual int CopyPixels(FBitmap* bmp, int conversion); // This will always ignore 'luminance'.
int CopyTranslatedPixels(FBitmap* bmp, PalEntry* remap); int CopyTranslatedPixels(FBitmap* bmp, PalEntry* remap);
@ -124,7 +125,8 @@ class FImageTexture : public FTexture
FImageSource *mImage; FImageSource *mImage;
public: public:
FImageTexture (FImageSource *image, const char *name = nullptr); FImageTexture (FImageSource *image, const char *name = nullptr);
virtual TArray<uint8_t> Get8BitPixels(bool alphatex); void Create8BitPixels(uint8_t* buffer) override;
const uint8_t* Get8BitPixels() override;
void SetImage(FImageSource *img) // This is only for the multipatch texture builder! void SetImage(FImageSource *img) // This is only for the multipatch texture builder!
{ {

View file

@ -0,0 +1,97 @@
/*
** imagehelpers.cpp
** Utilities for image conversion - mostly 8 bit paletted baggage
**
**---------------------------------------------------------------------------
** 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 "imagehelpers.h"
namespace ImageHelpers
{
uint8_t GrayMap[256];
PalEntry BaseColors[256];
PalEntry BasePalette[256]; // same as above, but with a being a proper alpha channel.
int WhiteIndex, BlackIndex;
ColorTable256k RGB256k;
int BestColor(int r, int g, int b, int first, int num)
{
const PalEntry* pal = BaseColors;
int bestcolor = first;
int bestdist = 257 * 257 + 257 * 257 + 257 * 257;
for (int color = first; color < num; color++)
{
if (pal[color].a > 0) continue; // marks a fullbright color which we should not pick here
int x = r - pal[color].r;
int y = g - pal[color].g;
int z = b - pal[color].b;
int dist = x * x + y * y + z * z;
if (dist < bestdist)
{
if (dist == 0)
return color;
bestdist = dist;
bestcolor = color;
}
}
return bestcolor;
}
void SetPalette(const PalEntry* colors)
{
for (int i = 0; i < 255; i++)
{
BasePalette[i] = BaseColors[i] = colors[i];
BasePalette[i].a = 255;
}
BasePalette[255] = BaseColors[255] = 0; // 255 is always translucent black - whatever color the original data has here
// Find white and black from the original palette so that they can be
// used to make an educated guess of the translucency % for a
// translucency map.
WhiteIndex = BestColor(255, 255, 255);
BlackIndex = BestColor(0, 0, 0);
// create the RGB666 lookup table
for (int r = 0; r < 64; r++)
for (int g = 0; g < 64; g++)
for (int b = 0; b < 64; b++)
RGB256k.RGB[r][g][b] = BestColor((r<<2)|(r>>4), (g<<2)|(g>>4), (b<<2)|(b>>4));
}
}

View file

@ -0,0 +1,144 @@
#pragma once
/*
** imagehelpers.h
** Utilities for image conversion - mostly 8 bit paletted baggage
**
**---------------------------------------------------------------------------
** 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 <stdint.h>
#include "tarray.h"
#include "palentry.h"
#include "textures/bitmap.h"
namespace ImageHelpers
{
union ColorTable256k
{
uint8_t RGB[64][64][64];
uint8_t All[64 * 64 * 64];
};
extern uint8_t GrayMap[256];
extern PalEntry BaseColors[256];
extern PalEntry BasePalette[256]; // same as above, but with a being a proper alpha channel.
extern int WhiteIndex, BlackIndex;
extern ColorTable256k RGB256k;
// Todo: This should not pick fullbright colors.
int BestColor(int r, int g, int b, int first = 0, int num = 255);
void SetPalette(const PalEntry* colors);
// Helpers for creating paletted images.
inline uint8_t *GetGraymap()
{
return GrayMap;
}
inline uint8_t RGBToPalettePrecise(bool wantluminance, int r, int g, int b, int a = 255)
{
return BestColor(r, g, b);
}
inline uint8_t RGBToPalette(bool wantluminance, int r, int g, int b, int a = 255)
{
return a < 128? 255 : RGB256k.RGB[r >> 2][g >> 2][b >> 2];
}
inline uint8_t RGBToPalette(bool wantluminance, PalEntry pe, bool hasalpha = true)
{
return RGBToPalette(wantluminance, pe.r, pe.g, pe.b, hasalpha? pe.a : 255);
}
//==========================================================================
//
// Converts a texture between row-major and column-major format
// by flipping it about the X=Y axis.
//
//==========================================================================
template<class T>
void FlipSquareBlock (T *block, int x)
{
for (int i = 0; i < x; ++i)
{
T *corner = block + x*i + i;
int count = x - i;
for (int j = 0; j < count; j++)
{
std::swap(corner[j], corner[j*x]);
}
}
}
inline void FlipSquareBlockRemap (uint8_t *block, int x, const uint8_t *remap)
{
for (int i = 0; i < x; ++i)
{
uint8_t *corner = block + x*i + i;
int count = x - i;
for (int j = 0; j < count; j++)
{
auto t = remap[corner[j]];
corner[j] = remap[corner[j*x]];
corner[j*x] = t;
}
}
}
template<class T>
void FlipNonSquareBlock (T *dst, const T *src, int x, int y, int srcpitch)
{
for (int i = 0; i < x; ++i)
{
for (int j = 0; j < y; ++j)
{
dst[i*y+j] = src[i+j*srcpitch];
}
}
}
inline void FlipNonSquareBlockRemap (uint8_t *dst, const uint8_t *src, int x, int y, int srcpitch, const uint8_t *remap)
{
for (int i = 0; i < x; ++i)
{
for (int j = 0; j < y; ++j)
{
dst[i*y+j] = remap[src[i+j*srcpitch]];
}
}
}
}

View file

@ -83,8 +83,12 @@ FBitmap FImageTexture::GetBgraBitmap(PalEntry *p, int *trans)
// //
//=========================================================================== //===========================================================================
TArray<uint8_t> FImageTexture::Get8BitPixels(bool alpha) const uint8_t *FImageTexture::Get8BitPixels()
{ {
return mImage->CreatePalettedPixels(0); return mImage->GetPalettedPixels();
} }
void FImageTexture::Create8BitPixels(uint8_t* buffer)
{
return mImage->CreatePalettedPixels(buffer);
}

View file

@ -390,11 +390,14 @@ bool FTexture::GetTranslucency()
// //
//=========================================================================== //===========================================================================
TArray<uint8_t> FTexture::Get8BitPixels(bool alphatex) const uint8_t* FTexture::Get8BitPixels()
{ {
TArray<uint8_t> Pixels(Width * Height, true); return nullptr; // most textures do not provide a static buffer.
memset(Pixels.Data(), 0, Width * Height); }
return Pixels;
void FTexture::Create8BitPixels(uint8_t *buffer)
{
// The base class does not fill the texture.
} }
#if 0 #if 0

View file

@ -157,7 +157,8 @@ public:
PalEntry GetSkyCapColor(bool bottom); PalEntry GetSkyCapColor(bool bottom);
// Returns the whole texture, stored in column-major order // Returns the whole texture, stored in column-major order
virtual TArray<uint8_t> Get8BitPixels(bool alphatex); virtual void Create8BitPixels(uint8_t* buffer);
virtual const uint8_t* Get8BitPixels();
virtual FBitmap GetBgraBitmap(PalEntry *remap, int *trans = nullptr); virtual FBitmap GetBgraBitmap(PalEntry *remap, int *trans = nullptr);
static int SmoothEdges(unsigned char * buffer,int w, int h); static int SmoothEdges(unsigned char * buffer,int w, int h);