From d0cbf21dbe74b0a08b7d579793a82d47f06a8014 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 23 May 2020 23:46:44 +0200 Subject: [PATCH] - swapped the image source implementations with GZDoom's. --- source/core/textures/formats/arttexture.cpp | 3 +- source/core/textures/formats/ddstexture.cpp | 34 +- source/core/textures/formats/jpegtexture.cpp | 53 ++- source/core/textures/formats/pcxtexture.cpp | 46 +-- source/core/textures/formats/pngtexture.cpp | 121 +++++-- source/core/textures/formats/stbtexture.cpp | 27 +- source/core/textures/formats/tgatexture.cpp | 49 +-- source/core/textures/image.cpp | 336 +++++++++++++++++-- source/core/textures/image.h | 46 ++- source/core/textures/imagehelpers.h | 37 +- source/core/textures/imagetexture.cpp | 20 +- source/core/textures/texture.cpp | 4 +- 12 files changed, 584 insertions(+), 192 deletions(-) diff --git a/source/core/textures/formats/arttexture.cpp b/source/core/textures/formats/arttexture.cpp index e84aeac76..35de68b95 100644 --- a/source/core/textures/formats/arttexture.cpp +++ b/source/core/textures/formats/arttexture.cpp @@ -41,7 +41,7 @@ #include "build.h" - +#if 0 //========================================================================== // // an AET texture @@ -172,3 +172,4 @@ int FArtTexture::CopyPixels(FBitmap *bmp, int conversion) return 0; } +#endif \ No newline at end of file diff --git a/source/core/textures/formats/ddstexture.cpp b/source/core/textures/formats/ddstexture.cpp index 58b2c3377..5f9b81fe1 100644 --- a/source/core/textures/formats/ddstexture.cpp +++ b/source/core/textures/formats/ddstexture.cpp @@ -163,9 +163,9 @@ class FDDSTexture : public FImageSource PIX_ARGB = 2 }; public: - FDDSTexture (FileReader &lump, void *surfdesc); + FDDSTexture (FileReader &lump, int lumpnum, void *surfdesc); - void CreatePalettedPixels(uint8_t *destbuffer) override; + TArray CreatePalettedPixels(int conversion) override; protected: uint32_t Format; @@ -219,7 +219,7 @@ static bool CheckDDS (FileReader &file) // //========================================================================== -FImageSource *DDSImage_TryCreate (FileReader &data) +FImageSource *DDSImage_TryCreate (FileReader &data, int lumpnum) { union { @@ -274,7 +274,7 @@ FImageSource *DDSImage_TryCreate (FileReader &data) { return NULL; } - return new FDDSTexture (data, &surfdesc); + return new FDDSTexture (data, lumpnum, &surfdesc); } //========================================================================== @@ -283,7 +283,8 @@ FImageSource *DDSImage_TryCreate (FileReader &data) // //========================================================================== -FDDSTexture::FDDSTexture (FileReader &lump, void *vsurfdesc) +FDDSTexture::FDDSTexture (FileReader &lump, int lumpnum, void *vsurfdesc) +: FImageSource(lumpnum) { DDSURFACEDESC2 *surf = (DDSURFACEDESC2 *)vsurfdesc; @@ -372,30 +373,32 @@ void FDDSTexture::CalcBitShift (uint32_t mask, uint8_t *lshiftp, uint8_t *rshift // //========================================================================== -void FDDSTexture::CreatePalettedPixels(uint8_t *buffer) +TArray FDDSTexture::CreatePalettedPixels(int conversion) { - auto lump = fileSystem.OpenFileReader(Name); - if (!lump.isOpen()) return; // Just leave the texture blank. + auto lump = fileSystem.OpenFileReader (SourceLump); + + TArray Pixels(Width*Height, true); lump.Seek (sizeof(DDSURFACEDESC2) + 4, FileReader::SeekSet); - int pmode = PIX_Palette; + int pmode = conversion == luminance ? PIX_Alphatex : PIX_Palette; if (Format >= 1 && Format <= 4) // RGB: Format is # of bytes per pixel { - ReadRGB (lump, buffer, pmode); + ReadRGB (lump, Pixels.Data(), pmode); } else if (Format == ID_DXT1) { - DecompressDXT1 (lump, buffer, pmode); + DecompressDXT1 (lump, Pixels.Data(), pmode); } else if (Format == ID_DXT3 || Format == ID_DXT2) { - DecompressDXT3 (lump, Format == ID_DXT2, buffer, pmode); + DecompressDXT3 (lump, Format == ID_DXT2, Pixels.Data(), pmode); } else if (Format == ID_DXT5 || Format == ID_DXT4) { - DecompressDXT5 (lump, Format == ID_DXT4, buffer, pmode); + DecompressDXT5 (lump, Format == ID_DXT4, Pixels.Data(), pmode); } + return Pixels; } //========================================================================== @@ -442,7 +445,7 @@ void FDDSTexture::ReadRGB (FileReader &lump, uint8_t *buffer, int pixelmode) 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); + *pixelp = ImageHelpers::RGBToPalette(pixelmode == PIX_Alphatex, r >> 24, g >> 24, b >> 24, a >> 24); } else { @@ -781,8 +784,7 @@ void FDDSTexture::DecompressDXT5 (FileReader &lump, bool premultiplied, uint8_t int FDDSTexture::CopyPixels(FBitmap *bmp, int conversion) { - auto lump = fileSystem.OpenFileReader(Name); - if (!lump.isOpen()) return -1; // Just leave the texture blank. + auto lump = fileSystem.OpenFileReader (SourceLump); uint8_t *TexBuffer = bmp->GetPixels(); diff --git a/source/core/textures/formats/jpegtexture.cpp b/source/core/textures/formats/jpegtexture.cpp index 243614e72..e07f4425d 100644 --- a/source/core/textures/formats/jpegtexture.cpp +++ b/source/core/textures/formats/jpegtexture.cpp @@ -35,20 +35,18 @@ */ #include - -#include "files.h" -#include "printf.h" -#include "bitmap.h" -#include "image.h" -#include "filesystem.h" -#include "imagehelpers.h" -#include "v_text.h" - extern "C" { #include } +#include "files.h" +#include "filesystem.h" +#include "printf.h" +#include "bitmap.h" +#include "imagehelpers.h" +#include "image.h" + struct FLumpSourceMgr : public jpeg_source_mgr { @@ -184,10 +182,10 @@ void JPEG_OutputMessage (j_common_ptr cinfo) class FJPEGTexture : public FImageSource { public: - FJPEGTexture (int width, int height); + FJPEGTexture (int lumpnum, int width, int height); - void CreatePalettedPixels(uint8_t* destbuffer) override; int CopyPixels(FBitmap *bmp, int conversion) override; + TArray CreatePalettedPixels(int conversion) override; }; //========================================================================== @@ -196,7 +194,7 @@ public: // //========================================================================== -FImageSource *JPEGImage_TryCreate(FileReader & data) +FImageSource *JPEGImage_TryCreate(FileReader & data, int lumpnum) { union { @@ -237,7 +235,7 @@ FImageSource *JPEGImage_TryCreate(FileReader & data) { return NULL; } - return new FJPEGTexture (BigShort(first4bytes.w[1]), BigShort(first4bytes.w[0])); + return new FJPEGTexture (lumpnum, BigShort(first4bytes.w[1]), BigShort(first4bytes.w[0])); } //========================================================================== @@ -246,7 +244,8 @@ FImageSource *JPEGImage_TryCreate(FileReader & data) // //========================================================================== -FJPEGTexture::FJPEGTexture (int width, int height) +FJPEGTexture::FJPEGTexture (int lumpnum, int width, int height) +: FImageSource(lumpnum) { bMasked = false; @@ -260,10 +259,9 @@ FJPEGTexture::FJPEGTexture (int width, int height) // //========================================================================== -void FJPEGTexture::CreatePalettedPixels(uint8_t *buffer) +TArray FJPEGTexture::CreatePalettedPixels(int conversion) { - auto lump = fileSystem.OpenFileReader(Name); - if (!lump.isOpen()) return; // Just leave the texture blank. + auto lump = fileSystem.OpenFileReader (SourceLump); JSAMPLE *buff = NULL; jpeg_decompress_struct cinfo; @@ -280,13 +278,14 @@ void FJPEGTexture::CreatePalettedPixels(uint8_t *buffer) FLumpSourceMgr sourcemgr(&lump, &cinfo); try { + bool doalpha = conversion == luminance; 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()); + Printf(TEXTCOLOR_ORANGE "Unsupported color format in %s\n", fileSystem.GetFileFullPath(SourceLump).GetChars()); } else { @@ -305,7 +304,7 @@ void FJPEGTexture::CreatePalettedPixels(uint8_t *buffer) case JCS_RGB: for (int x = Width; x > 0; --x) { - *out = ImageHelpers::RGBToPalette(false, in[0], in[1], in[2]); + *out = ImageHelpers::RGBToPalette(doalpha, in[0], in[1], in[2]); out += Height; in += 3; } @@ -313,7 +312,7 @@ void FJPEGTexture::CreatePalettedPixels(uint8_t *buffer) case JCS_GRAYSCALE: { - auto remap = GPalette.GrayMap; + auto remap = ImageHelpers::GetRemap(doalpha, true); for (int x = Width; x > 0; --x) { *out = remap[in[0]]; @@ -331,7 +330,7 @@ void FJPEGTexture::CreatePalettedPixels(uint8_t *buffer) 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 = ImageHelpers::RGBToPalette(doalpha, r, g, b); out += Height; in += 4; } @@ -345,7 +344,7 @@ void FJPEGTexture::CreatePalettedPixels(uint8_t *buffer) 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 = ImageHelpers::RGBToPalette(doalpha, r, g, b); out += Height; in += 4; } @@ -363,13 +362,14 @@ void FJPEGTexture::CreatePalettedPixels(uint8_t *buffer) } catch (int) { - Printf(TEXTCOLOR_ORANGE "JPEG error in %s\n", Name.GetChars()); + Printf(TEXTCOLOR_ORANGE "JPEG error in %s\n", fileSystem.GetFileFullPath(SourceLump).GetChars()); } jpeg_destroy_decompress(&cinfo); if (buff != NULL) { delete[] buff; } + return Pixels; } @@ -385,8 +385,7 @@ int FJPEGTexture::CopyPixels(FBitmap *bmp, int conversion) { PalEntry pe[256]; - auto lump = fileSystem.OpenFileReader(Name); - if (!lump.isOpen()) return -1; // Just leave the texture blank. + auto lump = fileSystem.OpenFileReader (SourceLump); jpeg_decompress_struct cinfo; jpeg_error_mgr jerr; @@ -406,7 +405,7 @@ int FJPEGTexture::CopyPixels(FBitmap *bmp, int conversion) (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()); + Printf(TEXTCOLOR_ORANGE "Unsupported color format in %s\n", fileSystem.GetFileFullPath(SourceLump).GetChars()); } else { @@ -454,7 +453,7 @@ int FJPEGTexture::CopyPixels(FBitmap *bmp, int conversion) } catch (int) { - Printf(TEXTCOLOR_ORANGE "JPEG error in %s\n", Name.GetChars()); + Printf(TEXTCOLOR_ORANGE "JPEG error in %s\n", fileSystem.GetFileFullPath(SourceLump).GetChars()); } jpeg_destroy_decompress(&cinfo); return 0; diff --git a/source/core/textures/formats/pcxtexture.cpp b/source/core/textures/formats/pcxtexture.cpp index 71f2df0cc..89d05e427 100644 --- a/source/core/textures/formats/pcxtexture.cpp +++ b/source/core/textures/formats/pcxtexture.cpp @@ -34,12 +34,11 @@ ** */ -#include "basics.h" #include "files.h" +#include "filesystem.h" #include "bitmap.h" #include "imagehelpers.h" #include "image.h" -#include "filesystem.h" //========================================================================== // @@ -82,7 +81,7 @@ struct PCXHeader class FPCXTexture : public FImageSource { public: - FPCXTexture (PCXHeader &); + FPCXTexture (int lumpnum, PCXHeader &); int CopyPixels(FBitmap *bmp, int conversion) override; @@ -92,7 +91,7 @@ protected: 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; + TArray CreatePalettedPixels(int conversion) override; }; @@ -102,7 +101,7 @@ protected: // //========================================================================== -FImageSource * PCXImage_TryCreate(FileReader & file) +FImageSource * PCXImage_TryCreate(FileReader & file, int lumpnum) { PCXHeader hdr; @@ -131,7 +130,7 @@ FImageSource * PCXImage_TryCreate(FileReader & file) file.Seek(0, FileReader::SeekSet); file.Read(&hdr, sizeof(hdr)); - return new FPCXTexture(hdr); + return new FPCXTexture(lumpnum, hdr); } //========================================================================== @@ -140,7 +139,8 @@ FImageSource * PCXImage_TryCreate(FileReader & file) // //========================================================================== -FPCXTexture::FPCXTexture(PCXHeader & hdr) +FPCXTexture::FPCXTexture(int lumpnum, PCXHeader & hdr) +: FImageSource(lumpnum) { bMasked = false; Width = LittleShort(hdr.xmax) - LittleShort(hdr.xmin) + 1; @@ -344,19 +344,20 @@ void FPCXTexture::ReadPCX24bits (uint8_t *dst, FileReader & lump, PCXHeader *hdr // //========================================================================== -void FPCXTexture::CreatePalettedPixels(uint8_t *buffer) +TArray FPCXTexture::CreatePalettedPixels(int conversion) { uint8_t PaletteMap[256]; PCXHeader header; int bitcount; - auto lump = fileSystem.OpenFileReader(Name); - if (!lump.isOpen()) return; // Just leave the texture blank. + auto lump = fileSystem.OpenFileReader(SourceLump); lump.Read(&header, sizeof(header)); bitcount = header.bitsPerPixel * header.numColorPlanes; + TArray Pixels(Width*Height, true); + bool alphatex = conversion == luminance; if (bitcount < 24) { if (bitcount < 8) @@ -365,17 +366,17 @@ void FPCXTexture::CreatePalettedPixels(uint8_t *buffer) { default: case 1: - PaletteMap[0] = GPalette.GrayMap[0]; - PaletteMap[1] = GPalette.GrayMap[255]; - ReadPCX1bit (buffer, lump, &header); + PaletteMap[0] = alphatex? 0 : GPalette.GrayMap[0]; + PaletteMap[1] = alphatex? 255 : GPalette.GrayMap[255]; + ReadPCX1bit (Pixels.Data(), 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]); + PaletteMap[i] = ImageHelpers::RGBToPalettePrecise(alphatex, header.palette[i * 3], header.palette[i * 3 + 1], header.palette[i * 3 + 2]); } - ReadPCX4bits (buffer, lump, &header); + ReadPCX4bits (Pixels.Data(), lump, &header); break; } } @@ -390,19 +391,20 @@ void FPCXTexture::CreatePalettedPixels(uint8_t *buffer) uint8_t r = lump.ReadUInt8(); uint8_t g = lump.ReadUInt8(); uint8_t b = lump.ReadUInt8(); - PaletteMap[i] = ImageHelpers::RGBToPalettePrecise(false, r, g, b); + PaletteMap[i] = ImageHelpers::RGBToPalettePrecise(alphatex, r, g, b); } lump.Seek(sizeof(header), FileReader::SeekSet); - ReadPCX8bits (buffer, lump, &header); + ReadPCX8bits (Pixels.Data(), lump, &header); } if (Width == Height) { - ImageHelpers::FlipSquareBlockRemap(buffer, Width, PaletteMap); + ImageHelpers::FlipSquareBlockRemap(Pixels.Data(), Width, PaletteMap); } else { TArray newpix(Width*Height, true); - ImageHelpers::FlipNonSquareBlockRemap (newpix.Data(), buffer, Width, Height, Width, PaletteMap); + ImageHelpers::FlipNonSquareBlockRemap (newpix.Data(), Pixels.Data(), Width, Height, Width, PaletteMap); + return newpix; } } else @@ -414,11 +416,12 @@ void FPCXTexture::CreatePalettedPixels(uint8_t *buffer) { for(int x=0; x < Width; x++) { - buffer[y + Height * x] = ImageHelpers::RGBToPalette(false, row[0], row[1], row[2]); + Pixels[y + Height * x] = ImageHelpers::RGBToPalette(alphatex, row[0], row[1], row[2]); row+=3; } } } + return Pixels; } //=========================================================================== @@ -436,8 +439,7 @@ int FPCXTexture::CopyPixels(FBitmap *bmp, int conversion) int bitcount; TArray Pixels; - auto lump = fileSystem.OpenFileReader(Name); - if (!lump.isOpen()) return -1; // Just leave the texture blank. + auto lump = fileSystem.OpenFileReader(SourceLump); lump.Read(&header, sizeof(header)); diff --git a/source/core/textures/formats/pngtexture.cpp b/source/core/textures/formats/pngtexture.cpp index 28d5b5e62..8041f7a00 100644 --- a/source/core/textures/formats/pngtexture.cpp +++ b/source/core/textures/formats/pngtexture.cpp @@ -53,20 +53,22 @@ class FPNGTexture : public FImageSource { public: - FPNGTexture (FileReader &lump, int width, int height, uint8_t bitdepth, uint8_t colortype, uint8_t interlace); + FPNGTexture (FileReader &lump, int lumpnum, int width, int height, uint8_t bitdepth, uint8_t colortype, uint8_t interlace); - void CreatePalettedPixels(uint8_t* buffer) override; int CopyPixels(FBitmap *bmp, int conversion) override; + TArray CreatePalettedPixels(int conversion) override; protected: + void ReadAlphaRemap(FileReader *lump, uint8_t *alpharemap); + uint8_t BitDepth; uint8_t ColorType; uint8_t Interlace; bool HaveTrans; uint16_t NonPaletteTrans[3]; - uint8_t PaletteMap[256]; - uint32_t PaletteSize = 0; + uint8_t *PaletteMap = nullptr; + int PaletteSize = 0; uint32_t StartOfIDAT = 0; uint32_t StartOfPalette = 0; }; @@ -78,7 +80,7 @@ protected: // //========================================================================== -FImageSource *PNGImage_TryCreate(FileReader & data) +FImageSource *PNGImage_TryCreate(FileReader & data, int lumpnum) { union { @@ -137,7 +139,7 @@ FImageSource *PNGImage_TryCreate(FileReader & data) } } - return new FPNGTexture (data, width, height, bitdepth, colortype, interlace); + return new FPNGTexture (data, lumpnum, width, height, bitdepth, colortype, interlace); } //========================================================================== @@ -146,13 +148,14 @@ FImageSource *PNGImage_TryCreate(FileReader & data) // //========================================================================== -FPNGTexture::FPNGTexture (FileReader &lump, int width, int height, +FPNGTexture::FPNGTexture (FileReader &lump, int lumpnum, int width, int height, uint8_t depth, uint8_t colortype, uint8_t interlace) - : BitDepth(depth), ColorType(colortype), Interlace(interlace), HaveTrans(false) +: FImageSource(lumpnum), + BitDepth(depth), ColorType(colortype), Interlace(interlace), HaveTrans(false) { union { - PalEntry palette[256]; + uint32_t palette[256]; uint8_t pngpal[256][3]; } p; uint8_t trans[256]; @@ -192,12 +195,12 @@ FPNGTexture::FPNGTexture (FileReader &lump, int width, int height, 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); + Printf ("X-Offset for PNG texture %s is bad: %d (0x%08x)\n", fileSystem.GetFileFullName (lumpnum), 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); + Printf ("Y-Offset for PNG texture %s is bad: %d (0x%08x)\n", fileSystem.GetFileFullName (lumpnum), ihoty, ihoty); ihoty = 0; } LeftOffset = ihotx; @@ -206,7 +209,7 @@ FPNGTexture::FPNGTexture (FileReader &lump, int width, int height, break; case MAKE_ID('P','L','T','E'): - PaletteSize = std::min (len / 3, 256); + PaletteSize = MIN (len / 3, 256); StartOfPalette = (uint32_t)lump.Tell(); lump.Read (p.pngpal, PaletteSize * 3); if (PaletteSize * 3 != (int)len) @@ -215,7 +218,7 @@ FPNGTexture::FPNGTexture (FileReader &lump, int width, int height, } for (i = PaletteSize - 1; i >= 0; --i) { - p.palette[i] = PalEntry(p.pngpal[i][0], p.pngpal[i][1], p.pngpal[i][2]); + p.palette[i] = MAKERGB(p.pngpal[i][0], p.pngpal[i][1], p.pngpal[i][2]); } break; @@ -246,27 +249,25 @@ FPNGTexture::FPNGTexture (FileReader &lump, int width, int height, { bMasked = true; PaletteSize = 256; - memcpy (PaletteMap, GPalette.GrayMap+1, 256); - PaletteMap[255] = 254; // cannot use 255. - PaletteMap[NonPaletteTrans[0]] = 255; + PaletteMap = (uint8_t*)ImageArena.Alloc(PaletteSize); + memcpy (PaletteMap, GPalette.GrayMap, 256); + PaletteMap[NonPaletteTrans[0]] = 0; } else { - memcpy(PaletteMap, GPalette.GrayMap, 256); + PaletteMap = GPalette.GrayMap; } break; case 3: // Paletted + PaletteMap = (uint8_t*)ImageArena.Alloc(PaletteSize); + MakeRemap ((uint32_t*)GPalette.BaseColors, p.palette, PaletteMap, trans, PaletteSize); for (i = 0; i < PaletteSize; ++i) { if (trans[i] == 0) { bMasked = true; - PaletteMap[i] = 255; - } - else - { - PaletteMap[i] = ColorMatcher.Pick(p.palette[i].r, p.palette[i].g, p.palette[i].b); + PaletteMap[i] = 0; } } break; @@ -287,13 +288,32 @@ FPNGTexture::FPNGTexture (FileReader &lump, int width, int height, // //========================================================================== -void FPNGTexture::CreatePalettedPixels(uint8_t *buffer) +void FPNGTexture::ReadAlphaRemap(FileReader *lump, uint8_t *alpharemap) +{ + auto p = lump->Tell(); + lump->Seek(StartOfPalette, FileReader::SeekSet); + for (int i = 0; i < PaletteSize; i++) + { + uint8_t r = lump->ReadUInt8(); + uint8_t g = lump->ReadUInt8(); + uint8_t b = lump->ReadUInt8(); + alpharemap[i] = PaletteMap[i] == 0 ? 0 : Luminance(r, g, b); + } + lump->Seek(p, FileReader::SeekSet); +} + +//========================================================================== +// +// +// +//========================================================================== + +TArray FPNGTexture::CreatePalettedPixels(int conversion) { FileReader *lump; FileReader lfr; - lfr = fileSystem.OpenFileReader(Name); - if (!lfr.isOpen()) return; + lfr = fileSystem.OpenFileReader(SourceLump); lump = 𝔩 TArray Pixels(Width*Height, true); @@ -308,18 +328,46 @@ void FPNGTexture::CreatePalettedPixels(uint8_t *buffer) lump->Read(&len, 4); lump->Read(&id, 4); + bool alphatex = conversion == luminance; 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); + if (conversion != luminance) + { + ImageHelpers::FlipSquareBlockRemap (Pixels.Data(), Width, PaletteMap); + } + else if (ColorType == 0) + { + ImageHelpers::FlipSquareBlock (Pixels.Data(), Width); + } + else + { + uint8_t alpharemap[256]; + ReadAlphaRemap(lump, alpharemap); + ImageHelpers::FlipSquareBlockRemap(Pixels.Data(), Width, alpharemap); + } } else { TArray newpix(Width*Height, true); - ImageHelpers::FlipNonSquareBlockRemap (newpix.Data(), Pixels.Data(), Width, Height, Width, PaletteMap); + if (conversion != luminance) + { + ImageHelpers::FlipNonSquareBlockRemap (newpix.Data(), Pixels.Data(), Width, Height, Width, PaletteMap); + } + else if (ColorType == 0) + { + ImageHelpers::FlipNonSquareBlock (newpix.Data(), Pixels.Data(), Width, Height, Width); + } + else + { + uint8_t alpharemap[256]; + ReadAlphaRemap(lump, alpharemap); + ImageHelpers::FlipNonSquareBlockRemap(newpix.Data(), Pixels.Data(), Width, Height, Width, alpharemap); + } + return newpix; } } else /* RGB and/or Alpha present */ @@ -346,11 +394,11 @@ void FPNGTexture::CreatePalettedPixels(uint8_t *buffer) { if (HaveTrans && in[0] == NonPaletteTrans[0] && in[1] == NonPaletteTrans[1] && in[2] == NonPaletteTrans[2]) { - *out++ = 255; + *out++ = 0; } else { - *out++ = ImageHelpers::RGBToPalette(false, in[0], in[1], in[2]); + *out++ = ImageHelpers::RGBToPalette(alphatex, in[0], in[1], in[2]); } in += pitch; } @@ -365,7 +413,7 @@ void FPNGTexture::CreatePalettedPixels(uint8_t *buffer) { for (y = Height; y > 0; --y) { - *out++ = PaletteMap[in[0]]; + *out++ = alphatex? ((in[0] * in[1]) / 255) : in[1] < 128 ? 0 : PaletteMap[in[0]]; in += pitch; } in -= backstep; @@ -379,7 +427,7 @@ void FPNGTexture::CreatePalettedPixels(uint8_t *buffer) { for (y = Height; y > 0; --y) { - *out++ = ImageHelpers::RGBToPalette(false, in[0], in[1], in[2], in[3]); + *out++ = ImageHelpers::RGBToPalette(alphatex, in[0], in[1], in[2], in[3]); in += pitch; } in -= backstep; @@ -389,6 +437,7 @@ void FPNGTexture::CreatePalettedPixels(uint8_t *buffer) delete[] tempix; } } + return Pixels; } //=========================================================================== @@ -409,9 +458,7 @@ int FPNGTexture::CopyPixels(FBitmap *bmp, int conversion) FileReader *lump; FileReader lfr; - lfr = fileSystem.OpenFileReader(Name); - if (!lfr.isOpen()) return -1; // Just leave the texture blank. - + lfr = fileSystem.OpenFileReader(SourceLump); lump = 𝔩 lump->Seek(33, FileReader::SeekSet); @@ -487,8 +534,8 @@ int FPNGTexture::CopyPixels(FBitmap *bmp, int conversion) } else { - bmp->CopyPixelDataRGB(0, 0, Pixels, Width, Height, 3, pixwidth, 0, CF_RGBT, - nullptr, NonPaletteTrans[0], NonPaletteTrans[1], NonPaletteTrans[2]); + bmp->CopyPixelDataRGB(0, 0, Pixels, Width, Height, 3, pixwidth, 0, CF_RGBT, nullptr, + NonPaletteTrans[0], NonPaletteTrans[1], NonPaletteTrans[2]); transpal = true; } break; @@ -512,6 +559,8 @@ int FPNGTexture::CopyPixels(FBitmap *bmp, int conversion) } +#include "textures.h" + //========================================================================== // // A savegame picture diff --git a/source/core/textures/formats/stbtexture.cpp b/source/core/textures/formats/stbtexture.cpp index 902f84547..ab84458cf 100644 --- a/source/core/textures/formats/stbtexture.cpp +++ b/source/core/textures/formats/stbtexture.cpp @@ -46,10 +46,10 @@ #include "files.h" +#include "filesystem.h" #include "bitmap.h" #include "imagehelpers.h" #include "image.h" -#include "filesystem.h" //========================================================================== // @@ -66,8 +66,8 @@ class FStbTexture : public FImageSource { public: - FStbTexture (int w, int h); - void CreatePalettedPixels(uint8_t *destbuffer) override; + FStbTexture (int lumpnum, int w, int h); + TArray CreatePalettedPixels(int conversion) override; int CopyPixels(FBitmap *bmp, int conversion) override; }; @@ -83,14 +83,14 @@ static stbi_io_callbacks callbacks = { // //========================================================================== -FImageSource *StbImage_TryCreate(FileReader & file) +FImageSource *StbImage_TryCreate(FileReader & file, int lumpnum) { int x, y, comp; file.Seek(0, FileReader::SeekSet); int result = stbi_info_from_callbacks(&callbacks, &file, &x, &y, &comp); if (result == 1) { - return new FStbTexture(x, y); + return new FStbTexture(lumpnum, x, y); } return nullptr; @@ -102,7 +102,8 @@ FImageSource *StbImage_TryCreate(FileReader & file) // //========================================================================== -FStbTexture::FStbTexture (int w, int h) +FStbTexture::FStbTexture (int lumpnum, int w, int h) + : FImageSource(lumpnum) { Width = w; Height = h; @@ -116,19 +117,21 @@ FStbTexture::FStbTexture (int w, int h) // //========================================================================== -void FStbTexture::CreatePalettedPixels(uint8_t *buffer) +TArray FStbTexture::CreatePalettedPixels(int conversion) { FBitmap bitmap; bitmap.Create(Width, Height); - CopyPixels(&bitmap, 0); + CopyPixels(&bitmap, conversion); const uint8_t *data = bitmap.GetPixels(); uint8_t *dest_p; int dest_adv = Height; int dest_rew = Width * Height - 1; - dest_p = buffer; + TArray Pixels(Width*Height, true); + dest_p = Pixels.Data(); + bool doalpha = conversion == luminance; // Convert the source image from row-major to column-major format and remap it for (int y = Height; y != 0; --y) { @@ -139,11 +142,12 @@ void FStbTexture::CreatePalettedPixels(uint8_t *buffer) int r = *data++; int a = *data++; if (a < 128) *dest_p = 0; - else *dest_p = ImageHelpers::RGBToPalette(false, r, g, b); + else *dest_p = ImageHelpers::RGBToPalette(doalpha, r, g, b); dest_p += dest_adv; } dest_p -= dest_rew; } + return Pixels; } //========================================================================== @@ -154,8 +158,7 @@ void FStbTexture::CreatePalettedPixels(uint8_t *buffer) int FStbTexture::CopyPixels(FBitmap *bmp, int conversion) { - auto lump = fileSystem.OpenFileReader(Name); - if (!lump.isOpen()) return -1; // Just leave the texture blank. + auto lump = fileSystem.OpenFileReader (SourceLump); int x, y, chan; auto image = stbi_load_from_callbacks(&callbacks, &lump, &x, &y, &chan, STBI_rgb_alpha); if (image) diff --git a/source/core/textures/formats/tgatexture.cpp b/source/core/textures/formats/tgatexture.cpp index 698526df0..80c7b04a8 100644 --- a/source/core/textures/formats/tgatexture.cpp +++ b/source/core/textures/formats/tgatexture.cpp @@ -34,11 +34,11 @@ */ #include "files.h" +#include "filesystem.h" #include "templates.h" #include "bitmap.h" -#include "image.h" -#include "filesystem.h" #include "imagehelpers.h" +#include "image.h" //========================================================================== @@ -77,13 +77,13 @@ struct TGAHeader class FTGATexture : public FImageSource { public: - FTGATexture (TGAHeader *); + FTGATexture (int lumpnum, TGAHeader *); int CopyPixels(FBitmap *bmp, int conversion) override; protected: void ReadCompressed(FileReader &lump, uint8_t * buffer, int bytesperpixel); - void CreatePalettedPixels(uint8_t *destbuffer) override; + TArray CreatePalettedPixels(int conversion) override; }; //========================================================================== @@ -92,7 +92,7 @@ protected: // //========================================================================== -FImageSource *TGAImage_TryCreate(FileReader & file) +FImageSource *TGAImage_TryCreate(FileReader & file, int lumpnum) { TGAHeader hdr; @@ -117,7 +117,7 @@ FImageSource *TGAImage_TryCreate(FileReader & file) hdr.width = LittleShort(hdr.width); hdr.height = LittleShort(hdr.height); - return new FTGATexture(&hdr); + return new FTGATexture(lumpnum, &hdr); } //========================================================================== @@ -126,7 +126,8 @@ FImageSource *TGAImage_TryCreate(FileReader & file) // //========================================================================== -FTGATexture::FTGATexture(TGAHeader * hdr) +FTGATexture::FTGATexture (int lumpnum, TGAHeader * hdr) +: FImageSource(lumpnum) { Width = hdr->width; Height = hdr->height; @@ -152,7 +153,7 @@ void FTGATexture::ReadCompressed(FileReader &lump, uint8_t * buffer, int bytespe { b&=~128; lump.Read(data, bytesperpixel); - for (int i=std::min(Size, (b+1)); i>0; i--) + for (int i=MIN(Size, (b+1)); i>0; i--) { buffer[0] = data[0]; if (bytesperpixel>=2) buffer[1] = data[1]; @@ -163,7 +164,7 @@ void FTGATexture::ReadCompressed(FileReader &lump, uint8_t * buffer, int bytespe } else { - lump.Read(buffer, std::min(Size, (b+1))*bytesperpixel); + lump.Read(buffer, MIN(Size, (b+1))*bytesperpixel); buffer += (b+1)*bytesperpixel; } Size -= b+1; @@ -176,11 +177,10 @@ void FTGATexture::ReadCompressed(FileReader &lump, uint8_t * buffer, int bytespe // //========================================================================== -void FTGATexture::CreatePalettedPixels(uint8_t *buffer) +TArray FTGATexture::CreatePalettedPixels(int conversion) { uint8_t PaletteMap[256]; - auto lump = fileSystem.OpenFileReader(Name); - if (!lump.isOpen()) return; + auto lump = fileSystem.OpenFileReader (SourceLump); TGAHeader hdr; uint16_t w; uint8_t r,g,b,a; @@ -229,22 +229,23 @@ void FTGATexture::CreatePalettedPixels(uint8_t *buffer) r=g=b=a=0; break; } - PaletteMap[i] = ImageHelpers::RGBToPalettePrecise(false, r, g, b, a); + PaletteMap[i] = ImageHelpers::RGBToPalettePrecise(conversion == luminance, r, g, b, a); } } int Size = Width * Height * (hdr.bpp>>3); + TArray buffer(Size, true); if (hdr.img_type < 4) // uncompressed { - lump.Read(buffer, Size); + lump.Read(buffer.Data(), Size); } else // compressed { - ReadCompressed(lump, buffer, hdr.bpp>>3); + ReadCompressed(lump, buffer.Data(), hdr.bpp>>3); } - uint8_t * ptr = buffer; + uint8_t * ptr = buffer.Data(); int step_x = (hdr.bpp>>3); int Pitch = Width * step_x; @@ -287,7 +288,7 @@ void FTGATexture::CreatePalettedPixels(uint8_t *buffer) for(int x=0;x> 10) & 0x1f) * 8, ((v >> 5) & 0x1f) * 8, (v & 0x1f) * 8); + Pixels[x*Height + y] = ImageHelpers::RGBToPalette(conversion == luminance, ((v >> 10) & 0x1f) * 8, ((v >> 5) & 0x1f) * 8, (v & 0x1f) * 8); p+=step_x; } } @@ -299,7 +300,7 @@ void FTGATexture::CreatePalettedPixels(uint8_t *buffer) uint8_t * p = ptr + y * Pitch; for(int x=0;x>3); - TArray sbuffer(Size, true); + TArray sbuffer(Size); if (hdr.img_type < 4) // uncompressed { diff --git a/source/core/textures/image.cpp b/source/core/textures/image.cpp index 5f1a517ac..76b0cc267 100644 --- a/source/core/textures/image.cpp +++ b/source/core/textures/image.cpp @@ -34,14 +34,160 @@ ** */ -#include "memarena.h" #include "bitmap.h" #include "image.h" -#include "files.h" #include "filesystem.h" -#include "imagehelpers.h" +#include "files.h" +#include "cmdlib.h" +#include "palettecontainer.h" +FMemArena FImageSource::ImageArena(32768); +TArrayFImageSource::ImageForLump; int FImageSource::NextID; +static PrecacheInfo precacheInfo; + +struct PrecacheDataPaletted +{ + TArray Pixels; + int RefCount; + int ImageID; +}; + +struct PrecacheDataRgba +{ + FBitmap Pixels; + int TransInfo; + int RefCount; + int ImageID; +}; + +// TMap doesn't handle this kind of data well. std::map neither. The linear search is still faster, even for a few 100 entries because it doesn't have to access the heap as often.. +TArray precacheDataPaletted; +TArray precacheDataRgba; + +//=========================================================================== +// +// the default just returns an empty texture. +// +//=========================================================================== + +TArray FImageSource::CreatePalettedPixels(int conversion) +{ + TArray Pixels(Width * Height, true); + memset(Pixels.Data(), 0, Width * Height); + return Pixels; +} + +PalettedPixels FImageSource::GetCachedPalettedPixels(int conversion) +{ + PalettedPixels ret; + + FString name; + fileSystem.GetFileShortName(name, SourceLump); + + std::pair *info = nullptr; + auto imageID = ImageID; + + // Do we have this image in the cache? + unsigned index = conversion != normal? UINT_MAX : precacheDataPaletted.FindEx([=](PrecacheDataPaletted &entry) { return entry.ImageID == imageID; }); + if (index < precacheDataPaletted.Size()) + { + auto cache = &precacheDataPaletted[index]; + + if (cache->RefCount > 1) + { + //Printf("returning reference to %s, refcount = %d\n", name.GetChars(), cache->RefCount); + ret.Pixels.Set(cache->Pixels.Data(), cache->Pixels.Size()); + cache->RefCount--; + } + else if (cache->Pixels.Size() > 0) + { + //Printf("returning contents of %s, refcount = %d\n", name.GetChars(), cache->RefCount); + ret.PixelStore = std::move(cache->Pixels); + ret.Pixels.Set(ret.PixelStore.Data(), ret.PixelStore.Size()); + precacheDataPaletted.Delete(index); + } + else + { + //Printf("something bad happened for %s, refcount = %d\n", name.GetChars(), cache->RefCount); + } + } + else + { + // The image wasn't cached. Now there's two possibilities: + auto info = precacheInfo.CheckKey(ImageID); + if (!info || info->second <= 1 || conversion != normal) + { + // This is either the only copy needed or some access outside the caching block. In these cases create a new one and directly return it. + //Printf("returning fresh copy of %s\n", name.GetChars()); + ret.PixelStore = CreatePalettedPixels(conversion); + ret.Pixels.Set(ret.PixelStore.Data(), ret.PixelStore.Size()); + } + else + { + //Printf("creating cached entry for %s, refcount = %d\n", name.GetChars(), info->second); + // This is the first time it gets accessed and needs to be placed in the cache. + PrecacheDataPaletted *pdp = &precacheDataPaletted[precacheDataPaletted.Reserve(1)]; + + pdp->ImageID = imageID; + pdp->RefCount = info->second - 1; + info->second = 0; + pdp->Pixels = CreatePalettedPixels(normal); + ret.Pixels.Set(pdp->Pixels.Data(), pdp->Pixels.Size()); + } + } + return ret; +} + +TArray FImageSource::GetPalettedPixels(int conversion) +{ + auto pix = GetCachedPalettedPixels(conversion); + if (pix.ownsPixels()) + { + // return the pixel store of the returned data directly if this was the last reference. + auto array = std::move(pix.PixelStore); + return array; + } + else + { + // If there are pending references, make a copy. + TArray array(pix.Pixels.Size(), true); + memcpy(array.Data(), pix.Pixels.Data(), array.Size()); + return array; + } +} + + + +//=========================================================================== +// +// 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::CopyPixels(FBitmap *bmp, int conversion) +{ + if (conversion == luminance) conversion = normal; // luminance images have no use as an RGB source. + PalEntry *palette = GPalette.BaseColors; + for(int i=1;i<256;i++) palette[i].a = 255; // set proper alpha values + auto ppix = CreatePalettedPixels(conversion); + bmp->CopyPixelData(0, 0, ppix.Data(), Width, Height, Height, 1, 0, palette, nullptr); + for(int i=1;i<256;i++) palette[i].a = 0; + return 0; +} + +int FImageSource::CopyTranslatedPixels(FBitmap *bmp, const PalEntry *remap) +{ + auto ppix = CreatePalettedPixels(false); + bmp->CopyPixelData(0, 0, ppix.Data(), Width, Height, Height, 1, 0, remap, nullptr); + return 0; +} //========================================================================== // @@ -49,46 +195,184 @@ int FImageSource::NextID; // //========================================================================== -typedef FImageSource * (*CreateFunc)(FileReader & file); +FBitmap FImageSource::GetCachedBitmap(const PalEntry *remap, int conversion, int *ptrans) +{ + FBitmap ret; + + FString name; + int trans = -1; + fileSystem.GetFileShortName(name, SourceLump); + + std::pair *info = nullptr; + auto imageID = ImageID; + + if (remap != nullptr) + { + // Remapped images are never run through the cache because they would complicate matters too much for very little gain. + // Translated images are normally sprites which normally just consist of a single image and use no composition. + // Additionally, since translation requires the base palette, the really time consuming stuff will never be subjected to it. + ret.Create(Width, Height); + trans = CopyTranslatedPixels(&ret, remap); + } + else + { + if (conversion == luminance) conversion = normal; // luminance has no meaning for true color. + // Do we have this image in the cache? + unsigned index = conversion != normal? UINT_MAX : precacheDataRgba.FindEx([=](PrecacheDataRgba &entry) { return entry.ImageID == imageID; }); + if (index < precacheDataRgba.Size()) + { + auto cache = &precacheDataRgba[index]; + + trans = cache->TransInfo; + if (cache->RefCount > 1) + { + //Printf("returning reference to %s, refcount = %d\n", name.GetChars(), cache->RefCount); + ret.Copy(cache->Pixels, false); + cache->RefCount--; + } + else if (cache->Pixels.GetPixels()) + { + //Printf("returning contents of %s, refcount = %d\n", name.GetChars(), cache->RefCount); + ret = std::move(cache->Pixels); + precacheDataRgba.Delete(index); + } + else + { + // This should never happen if the function is implemented correctly + //Printf("something bad happened for %s, refcount = %d\n", name.GetChars(), cache->RefCount); + ret.Create(Width, Height); + trans = CopyPixels(&ret, normal); + } + } + else + { + // The image wasn't cached. Now there's two possibilities: + auto info = precacheInfo.CheckKey(ImageID); + if (!info || info->first <= 1 || conversion != normal) + { + // This is either the only copy needed or some access outside the caching block. In these cases create a new one and directly return it. + //Printf("returning fresh copy of %s\n", name.GetChars()); + ret.Create(Width, Height); + trans = CopyPixels(&ret, conversion); + } + else + { + //Printf("creating cached entry for %s, refcount = %d\n", name.GetChars(), info->first); + // This is the first time it gets accessed and needs to be placed in the cache. + PrecacheDataRgba *pdr = &precacheDataRgba[precacheDataRgba.Reserve(1)]; + + pdr->ImageID = imageID; + pdr->RefCount = info->first - 1; + info->first = 0; + pdr->Pixels.Create(Width, Height); + trans = pdr->TransInfo = CopyPixels(&pdr->Pixels, normal); + ret.Copy(pdr->Pixels, false); + } + } + } + if (ptrans) *ptrans = trans; + return ret; +} + +//========================================================================== +// +// +// +//========================================================================== + +void FImageSource::CollectForPrecache(PrecacheInfo &info, bool requiretruecolor) +{ + auto val = info.CheckKey(ImageID); + bool tc = requiretruecolor; + if (val) + { + val->first += tc; + val->second += !tc; + } + else + { + auto pair = std::make_pair(tc, !tc); + info.Insert(ImageID, pair); + } +} + +void FImageSource::BeginPrecaching() +{ + precacheInfo.Clear(); +} + +void FImageSource::EndPrecaching() +{ + precacheDataPaletted.Clear(); + precacheDataRgba.Clear(); +} + +void FImageSource::RegisterForPrecache(FImageSource *img, bool requiretruecolor) +{ + img->CollectForPrecache(precacheInfo, requiretruecolor); +} + +//========================================================================== +// +// +// +//========================================================================== + +typedef FImageSource * (*CreateFunc)(FileReader & file, int lumpnum); struct TexCreateInfo { CreateFunc TryCreate; + bool checkflat; }; -FImageSource *PNGImage_TryCreate(FileReader &); -FImageSource *JPEGImage_TryCreate(FileReader &); -FImageSource *DDSImage_TryCreate(FileReader &); -FImageSource *PCXImage_TryCreate(FileReader &); -FImageSource *TGAImage_TryCreate(FileReader &); -FImageSource *ArtImage_TryCreate(FileReader &); -FImageSource *StbImage_TryCreate(FileReader &); +FImageSource *PNGImage_TryCreate(FileReader &, int lumpnum); +FImageSource *JPEGImage_TryCreate(FileReader &, int lumpnum); +FImageSource *DDSImage_TryCreate(FileReader &, int lumpnum); +FImageSource *PCXImage_TryCreate(FileReader &, int lumpnum); +FImageSource *TGAImage_TryCreate(FileReader &, int lumpnum); +FImageSource *StbImage_TryCreate(FileReader &, int lumpnum); // Examines the lump contents to decide what type of texture to create, // and creates the texture. -FImageSource * FImageSource::GetImage(const char *name) +FImageSource * FImageSource::GetImage(int lumpnum, bool isflat) { static TexCreateInfo CreateInfo[] = { - { PNGImage_TryCreate }, - { JPEGImage_TryCreate }, - { DDSImage_TryCreate }, - { PCXImage_TryCreate }, - { StbImage_TryCreate }, - { ArtImage_TryCreate }, - { nullptr } + { PNGImage_TryCreate, false }, + { JPEGImage_TryCreate, false }, + { DDSImage_TryCreate, false }, + { PCXImage_TryCreate, false }, + { StbImage_TryCreate, false }, + { TGAImage_TryCreate, false }, }; - auto data = fileSystem.OpenFileReader(name); - if (!data.isOpen()) return nullptr; + if (lumpnum == -1) return nullptr; - for (size_t i = 0; CreateInfo[i].TryCreate; i++) + unsigned size = ImageForLump.Size(); + if (size <= (unsigned)lumpnum) { - auto image = CreateInfo[i].TryCreate(data); - if (image != nullptr) + // Hires textures can be added dynamically to the end of the lump array, so this must be checked each time. + ImageForLump.Resize(lumpnum + 1); + for (; size < ImageForLump.Size(); size++) ImageForLump[size] = nullptr; + } + // An image for this lump already exists. We do not need another one. + if (ImageForLump[lumpnum] != nullptr) return ImageForLump[lumpnum]; + + auto data = fileSystem.OpenFileReader(lumpnum); + if (!data.isOpen()) + return nullptr; + + for (size_t i = 0; i < countof(CreateInfo); i++) + { + if (!CreateInfo[i].checkflat || isflat) { - image->Name = name; - return image; + auto image = CreateInfo[i].TryCreate(data, lumpnum); + if (image != nullptr) + { + ImageForLump[lumpnum] = image; + return image; + } } } return nullptr; diff --git a/source/core/textures/image.h b/source/core/textures/image.h index 4bca8ed2b..19db61ffa 100644 --- a/source/core/textures/image.h +++ b/source/core/textures/image.h @@ -10,6 +10,16 @@ class FImageSource; using PrecacheInfo = TMap>; +// Doom patch format header +struct patch_t +{ + int16_t width; // bounding box size + int16_t height; + int16_t leftoffset; // pixels to the left of origin + int16_t topoffset; // pixels below the origin + uint32_t columnofs[1]; // only [width] used +}; + struct PalettedPixels { friend class FImageSource; @@ -27,8 +37,11 @@ private: // All it can do is provide raw image data to its users. class FImageSource { + friend class FBrightmapTexture; protected: + static FMemArena ImageArena; + static TArrayImageForLump; static int NextID; int SourceLump; @@ -36,14 +49,17 @@ protected: 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. + virtual TArray CreatePalettedPixels(int conversion); + virtual int CopyPixels(FBitmap *bmp, int conversion); // This will always ignore 'luminance'. + int CopyTranslatedPixels(FBitmap *bmp, const PalEntry *remap); + + public: - virtual ~FImageSource() = default; void CopySize(FImageSource &other) { Width = other.Width; @@ -53,16 +69,30 @@ public: SourceLump = other.SourceLump; } + // Images are statically allocated and freed in bulk. None of the subclasses may hold any destructible data. + void *operator new(size_t block) { return ImageArena.Alloc(block); } + void operator delete(void *block) {} + 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 void CreatePalettedPixels(uint8_t *destbuffer) = 0; - virtual int CopyPixels(FBitmap* bmp, int conversion) = 0; // This will always ignore 'luminance'. + // Either returns a reference to the cache, or a newly created item. The return of this has to be considered transient. If you need to store the result, use GetPalettedPixels + PalettedPixels GetCachedPalettedPixels(int conversion); + + // tries to get a buffer from the cache. If not available, create a new one. If further references are pending, create a copy. + TArray GetPalettedPixels(int conversion); + + + // Unlile for paletted images there is no variant here that returns a persistent bitmap, because all users have to process the returned image into another format. + FBitmap GetCachedBitmap(const PalEntry *remap, int conversion, int *trans = nullptr); + + static void ClearImages() { ImageArena.FreeAll(); ImageForLump.Clear(); NextID = 0; } + static FImageSource * GetImage(int lumpnum, bool checkflat); + // Conversion option @@ -74,6 +104,7 @@ public: }; FImageSource(int sourcelump = -1) : SourceLump(sourcelump) { ImageID = ++NextID; } + virtual ~FImageSource() {} int GetWidth() const { @@ -110,6 +141,11 @@ public: { return bUseGamePalette; } + + virtual void CollectForPrecache(PrecacheInfo &info, bool requiretruecolor); + static void BeginPrecaching(); + static void EndPrecaching(); + static void RegisterForPrecache(FImageSource *img, bool requiretruecolor); }; //========================================================================== diff --git a/source/core/textures/imagehelpers.h b/source/core/textures/imagehelpers.h index 57878ea17..8923f92db 100644 --- a/source/core/textures/imagehelpers.h +++ b/source/core/textures/imagehelpers.h @@ -39,24 +39,49 @@ #include #include "tarray.h" -#include "palentry.h" +#include "colormatcher.h" #include "bitmap.h" -#include "palutil.h" #include "palettecontainer.h" #include "v_colortables.h" namespace ImageHelpers { - extern int alphaThreshold; - + // Helpers for creating paletted images. + inline uint8_t *GetRemap(bool wantluminance, bool srcisgrayscale = false) + { + if (wantluminance) + { + return srcisgrayscale ? GPalette.GrayRamp.Remap : GPalette.GrayscaleMap.Remap; + } + else + { + return srcisgrayscale ? GPalette.GrayMap : GPalette.Remap; + } + } + inline uint8_t RGBToPalettePrecise(bool wantluminance, int r, int g, int b, int a = 255) { - return BestColor((uint32_t*)GPalette.BaseColors, r, g, b); + if (wantluminance) + { + return (uint8_t)Luminance(r, g, b) * a / 255; + } + else + { + return ColorMatcher.Pick(r, g, b); + } } inline uint8_t RGBToPalette(bool wantluminance, int r, int g, int b, int a = 255) { - return a < alphaThreshold? 255 : RGB256k.RGB[r >> 2][g >> 2][b >> 2]; + if (wantluminance) + { + // This is the same formula the OpenGL renderer uses for grayscale textures with an alpha channel. + return (uint8_t)(Luminance(r, g, b) * a / 255); + } + else + { + return a < 128? 0 : RGB256k.RGB[r >> 2][g >> 2][b >> 2]; + } } inline uint8_t RGBToPalette(bool wantluminance, PalEntry pe, bool hasalpha = true) diff --git a/source/core/textures/imagetexture.cpp b/source/core/textures/imagetexture.cpp index 64570775c..902bfce43 100644 --- a/source/core/textures/imagetexture.cpp +++ b/source/core/textures/imagetexture.cpp @@ -71,20 +71,7 @@ FImageTexture::FImageTexture(FImageSource *img, const char *name) FBitmap FImageTexture::GetBgraBitmap(const PalEntry *p, int *trans) { - FBitmap bmp; - bmp.Create(Size.x, Size.y); - if (p == nullptr) - { - mImage->CopyPixels(&bmp, 0); - } - else - { - // For different base palettes the image needs to be downconverted. - TArray ppix(Size.x * Size.y, true); - mImage->CreatePalettedPixels(ppix.Data()); - bmp.CopyPixelData(0, 0, ppix.Data(), Size.x, Size.y, Size.y, 1, 0, p); - } - return bmp; + return mImage->GetCachedBitmap(p, FImageSource::normal, trans); } //=========================================================================== @@ -95,6 +82,7 @@ FBitmap FImageTexture::GetBgraBitmap(const PalEntry *p, int *trans) void FImageTexture::Create8BitPixels(uint8_t* buffer) { - ImageHelpers::alphaThreshold = alphaThreshold; - return mImage->CreatePalettedPixels(buffer); + //ImageHelpers::alphaThreshold = alphaThreshold; + auto buf = mImage->GetPalettedPixels(FImageSource::normal); + memcpy(buffer, buf.Data(), buf.Size()); } diff --git a/source/core/textures/texture.cpp b/source/core/textures/texture.cpp index 5f6070fe6..e7d3bfe24 100644 --- a/source/core/textures/texture.cpp +++ b/source/core/textures/texture.cpp @@ -56,7 +56,9 @@ FTexture *CreateBrightmapTexture(FImageSource*); // and creates the texture. FTexture * FTexture::CreateTexture(const char *name) { - auto image = FImageSource::GetImage(name); + int lump = fileSystem.FindFile(name); + if (lump < 0) return nullptr; + auto image = FImageSource::GetImage(lump, false); if (image != nullptr) { FTexture *tex = new FImageTexture(image);