diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 73cb8fac7..7c093126c 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -1055,6 +1055,7 @@ set (PCH_SOURCES common/textures/texture.cpp common/textures/image.cpp common/textures/imagetexture.cpp + common/textures/imagehelpers.cpp common/textures/formats/buildtexture.cpp common/textures/formats/ddstexture.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("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\\Utility" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/utility/.+") 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/.+") diff --git a/source/common/textures/formats/arttexture.cpp b/source/common/textures/formats/arttexture.cpp index 0092b32b2..63c9cb7f0 100644 --- a/source/common/textures/formats/arttexture.cpp +++ b/source/common/textures/formats/arttexture.cpp @@ -52,6 +52,7 @@ class FArtTexture : public FImageSource public: FArtTexture (int width, int height, int p); + void CreatePalettedPixels(uint8_t* buffer); int CopyPixels(FBitmap *bmp, int conversion) override; 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; } +//========================================================================== +// +// 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 diff --git a/source/common/textures/formats/buildtexture.cpp b/source/common/textures/formats/buildtexture.cpp index 5ccd10c25..495ecfd97 100644 --- a/source/common/textures/formats/buildtexture.cpp +++ b/source/common/textures/formats/buildtexture.cpp @@ -53,7 +53,7 @@ class FBuildTexture : public FImageSource { public: FBuildTexture (const FString &pathprefix, int tilenum, const uint8_t *pixels, int width, int height, int left, int top); - TArray CreatePalettedPixels(int conversion) override; + //void CreatePalettedPixels(uint8_t *destbuffer) override; protected: const uint8_t *RawPixels; @@ -75,12 +75,14 @@ FBuildTexture::FBuildTexture(const FString &pathprefix, int tilenum, const uint8 TopOffset = top; } +#if 0 TArray FBuildTexture::CreatePalettedPixels(int conversion) { TArray Pixels(Width * Height, true); memcpy(Pixels.Data(), RawPixels, Width * Height); return Pixels; } +#endif #if 0 //=========================================================================== diff --git a/source/common/textures/formats/ddstexture.cpp b/source/common/textures/formats/ddstexture.cpp index c9e1743e1..42e26fc31 100644 --- a/source/common/textures/formats/ddstexture.cpp +++ b/source/common/textures/formats/ddstexture.cpp @@ -51,6 +51,7 @@ #include #include "files.h" #include "bitmap.h" +#include "imagehelpers.h" #include "image.h" #include "m_png.h" #include "cache1d.h" @@ -163,6 +164,8 @@ class FDDSTexture : public FImageSource public: FDDSTexture (FileReader &lump, void *surfdesc); + void CreatePalettedPixels(uint8_t *destbuffer) override; + protected: uint32_t Format; @@ -362,6 +365,38 @@ void FDDSTexture::CalcBitShift (uint32_t mask, uint8_t *lshiftp, uint8_t *rshift *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! @@ -400,7 +435,20 @@ void FDDSTexture::ReadRGB (FileReader &lump, uint8_t *buffer, int pixelmode) } 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 { @@ -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. 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. 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. 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. @@ -671,7 +719,7 @@ void FDDSTexture::DecompressDXT5 (FileReader &lump, bool premultiplied, uint8_t // Pick colors from the palette for each of the four colors. 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. for (y = 0; y < 4; ++y) diff --git a/source/common/textures/formats/jpegtexture.cpp b/source/common/textures/formats/jpegtexture.cpp index 7902c33e0..2acad87b5 100644 --- a/source/common/textures/formats/jpegtexture.cpp +++ b/source/common/textures/formats/jpegtexture.cpp @@ -40,6 +40,7 @@ #include "bitmap.h" #include "image.h" #include "cache1d.h" +#include "imagehelpers.h" extern "C" { @@ -183,6 +184,7 @@ class FJPEGTexture : public FImageSource public: FJPEGTexture (int width, int height); + void CreatePalettedPixels(uint8_t* destbuffer) override; int CopyPixels(FBitmap *bmp, int conversion) override; }; @@ -250,6 +252,125 @@ FJPEGTexture::FJPEGTexture (int width, int 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 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 diff --git a/source/common/textures/formats/pcxtexture.cpp b/source/common/textures/formats/pcxtexture.cpp index 9a15192e2..6966a256a 100644 --- a/source/common/textures/formats/pcxtexture.cpp +++ b/source/common/textures/formats/pcxtexture.cpp @@ -37,6 +37,7 @@ #include "basics.h" #include "files.h" #include "bitmap.h" +#include "imagehelpers.h" #include "image.h" #include "cache1d.h" @@ -90,6 +91,8 @@ protected: 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); + + 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 newpix(Width*Height, true); + ImageHelpers::FlipNonSquareBlockRemap (newpix.Data(), buffer, Width, Height, Width, PaletteMap); + } + } + else + { + TArray buffer(Width*Height * 3, true); + uint8_t * row = buffer.Data(); + ReadPCX24bits (row, lump, &header, 3); + for(int y=0; y (len / 3, 256); 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; case MAKE_ID('t','R','N','S'): @@ -219,16 +237,36 @@ FPNGTexture::FPNGTexture (FileReader &lump, int width, int height, { case 4: // Grayscale + Alpha bMasked = true; - break; + // intentional fall-through case 0: // Grayscale if (colortype == 0 && HaveTrans && NonPaletteTrans[0] < 256) { 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; 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; 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 = 𝔩 + + TArray 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 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 diff --git a/source/common/textures/formats/stbtexture.cpp b/source/common/textures/formats/stbtexture.cpp index 668871a50..e0dd82f63 100644 --- a/source/common/textures/formats/stbtexture.cpp +++ b/source/common/textures/formats/stbtexture.cpp @@ -47,6 +47,7 @@ #include "files.h" #include "bitmap.h" +#include "imagehelpers.h" #include "image.h" #include "cache1d.h" @@ -66,6 +67,7 @@ class FStbTexture : public FImageSource public: FStbTexture (int w, int h); + void CreatePalettedPixels(uint8_t *destbuffer) 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) { - auto lump = kopenFileReader(Name, 0); + auto lump = kopenFileReader(Name, 0); + if (!lump.isOpen()) return -1; // Just leave the texture blank. int x, y, chan; auto image = stbi_load_from_callbacks(&callbacks, &lump, &x, &y, &chan, STBI_rgb_alpha); if (image) diff --git a/source/common/textures/formats/tgatexture.cpp b/source/common/textures/formats/tgatexture.cpp index b8df67dcb..de5ecb90b 100644 --- a/source/common/textures/formats/tgatexture.cpp +++ b/source/common/textures/formats/tgatexture.cpp @@ -38,6 +38,7 @@ #include "bitmap.h" #include "image.h" #include "cache1d.h" +#include "imagehelpers.h" //========================================================================== @@ -82,6 +83,7 @@ public: protected: 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 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>=1; + for(int y=0;y> 10) & 0x1f) * 8, ((v >> 5) & 0x1f) * 8, (v & 0x1f) * 8); + p+=step_x; + } + } + break; + + case 24: + for(int y=0;y>3); - TArray sbuffer(Size); + TArray sbuffer(Size, true); if (hdr.img_type < 4) // uncompressed { diff --git a/source/common/textures/image.cpp b/source/common/textures/image.cpp index 1219f45e4..c899d69f9 100644 --- a/source/common/textures/image.cpp +++ b/source/common/textures/image.cpp @@ -39,6 +39,7 @@ #include "image.h" #include "files.h" #include "cache1d.h" +#include "imagehelpers.h" int FImageSource::NextID; @@ -48,13 +49,15 @@ int FImageSource::NextID; // //=========================================================================== -TArray FImageSource::CreatePalettedPixels(int conversion) +void FImageSource::CreatePalettedPixels(uint8_t *buffer) { - TArray Pixels(Width * Height, true); - memset(Pixels.Data(), 0, Width * Height); - return Pixels; + memset(buffer, 0, Width * Height); } +const uint8_t* FImageSource::GetPalettedPixels() +{ + return nullptr; +} //=========================================================================== // @@ -70,14 +73,21 @@ TArray FImageSource::CreatePalettedPixels(int conversion) int FImageSource::CopyTranslatedPixels(FBitmap *bmp, PalEntry *remap) { - auto ppix = CreatePalettedPixels(false); - bmp->CopyPixelData(0, 0, ppix.Data(), Width, Height, Height, 1, 0, remap); + TArray buffer; + 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; } 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. } diff --git a/source/common/textures/image.h b/source/common/textures/image.h index 3aa27bb99..d79af8e4e 100644 --- a/source/common/textures/image.h +++ b/source/common/textures/image.h @@ -61,7 +61,8 @@ public: // 'noremap0' will only be looked at by FPatchTexture and forwarded by FMultipatchTexture. static FImageSource * GetImage(const char *name); - virtual TArray 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'. int CopyTranslatedPixels(FBitmap* bmp, PalEntry* remap); @@ -124,7 +125,8 @@ class FImageTexture : public FTexture FImageSource *mImage; public: FImageTexture (FImageSource *image, const char *name = nullptr); - virtual TArray 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! { diff --git a/source/common/textures/imagehelpers.cpp b/source/common/textures/imagehelpers.cpp new file mode 100644 index 000000000..68670903d --- /dev/null +++ b/source/common/textures/imagehelpers.cpp @@ -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)); + } + + +} \ No newline at end of file diff --git a/source/common/textures/imagehelpers.h b/source/common/textures/imagehelpers.h new file mode 100644 index 000000000..d06708fd2 --- /dev/null +++ b/source/common/textures/imagehelpers.h @@ -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 +#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 + 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 + 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]]; + } + } + } + +} diff --git a/source/common/textures/imagetexture.cpp b/source/common/textures/imagetexture.cpp index 77d208ba1..f647379f4 100644 --- a/source/common/textures/imagetexture.cpp +++ b/source/common/textures/imagetexture.cpp @@ -83,8 +83,12 @@ FBitmap FImageTexture::GetBgraBitmap(PalEntry *p, int *trans) // //=========================================================================== -TArray 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); +} diff --git a/source/common/textures/texture.cpp b/source/common/textures/texture.cpp index 1f98d54fc..95759a959 100644 --- a/source/common/textures/texture.cpp +++ b/source/common/textures/texture.cpp @@ -390,11 +390,14 @@ bool FTexture::GetTranslucency() // //=========================================================================== -TArray FTexture::Get8BitPixels(bool alphatex) +const uint8_t* FTexture::Get8BitPixels() { - TArray Pixels(Width * Height, true); - memset(Pixels.Data(), 0, Width * Height); - return Pixels; + return nullptr; // most textures do not provide a static buffer. +} + +void FTexture::Create8BitPixels(uint8_t *buffer) +{ + // The base class does not fill the texture. } #if 0 diff --git a/source/common/textures/textures.h b/source/common/textures/textures.h index b3a700826..71ea3646a 100644 --- a/source/common/textures/textures.h +++ b/source/common/textures/textures.h @@ -157,7 +157,8 @@ public: PalEntry GetSkyCapColor(bool bottom); // Returns the whole texture, stored in column-major order - virtual TArray Get8BitPixels(bool alphatex); + virtual void Create8BitPixels(uint8_t* buffer); + virtual const uint8_t* Get8BitPixels(); virtual FBitmap GetBgraBitmap(PalEntry *remap, int *trans = nullptr); static int SmoothEdges(unsigned char * buffer,int w, int h);