/* ** pcxtexture.cpp ** Texture class for PCX images ** **--------------------------------------------------------------------------- ** Copyright 2005 David HENRY ** Copyright 2006 Christoph Oelckers ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** ** */ #include "doomtype.h" #include "files.h" #include "w_wad.h" #include "templates.h" #include "bitmap.h" #include "colormatcher.h" #include "v_video.h" #include "textures/textures.h" //========================================================================== // // PCX file header // //========================================================================== #pragma pack(1) struct PCXHeader { uint8_t manufacturer; uint8_t version; uint8_t encoding; uint8_t bitsPerPixel; uint16_t xmin, ymin; uint16_t xmax, ymax; uint16_t horzRes, vertRes; uint8_t palette[48]; uint8_t reserved; uint8_t numColorPlanes; uint16_t bytesPerScanLine; uint16_t paletteType; uint16_t horzSize, vertSize; uint8_t padding[54]; } FORCE_PACKED; #pragma pack() //========================================================================== // // a PCX texture // //========================================================================== class FPCXTexture : public FWorldTexture { public: FPCXTexture (int lumpnum, PCXHeader &); FTextureFormat GetFormat () override; int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf = NULL) override; bool UseBasePalette() override; protected: void ReadPCX1bit (uint8_t *dst, FileReader & lump, PCXHeader *hdr); void ReadPCX4bits (uint8_t *dst, FileReader & lump, PCXHeader *hdr); void ReadPCX8bits (uint8_t *dst, FileReader & lump, PCXHeader *hdr); void ReadPCX24bits (uint8_t *dst, FileReader & lump, PCXHeader *hdr, int planes); uint8_t *MakeTexture (FRenderStyle style) override; }; //========================================================================== // // // //========================================================================== FTexture * PCXTexture_TryCreate(FileReader & file, int lumpnum) { PCXHeader hdr; file.Seek(0, FileReader::SeekSet); if (file.Read(&hdr, sizeof(hdr)) != sizeof(hdr)) { return NULL; } hdr.xmin = LittleShort(hdr.xmin); hdr.xmax = LittleShort(hdr.xmax); hdr.bytesPerScanLine = LittleShort(hdr.bytesPerScanLine); if (hdr.manufacturer != 10 || hdr.encoding != 1) return NULL; if (hdr.version != 0 && hdr.version != 2 && hdr.version != 3 && hdr.version != 4 && hdr.version != 5) return NULL; if (hdr.bitsPerPixel != 1 && hdr.bitsPerPixel != 8 && hdr.bitsPerPixel != 4) return NULL; if (hdr.bitsPerPixel == 1 && hdr.numColorPlanes !=1 && hdr.numColorPlanes != 4) return NULL; if (hdr.bitsPerPixel == 8 && hdr.bytesPerScanLine != ((hdr.xmax - hdr.xmin + 2)&~1)) return NULL; for (int i = 0; i < 54; i++) { if (hdr.padding[i] != 0) return NULL; } file.Seek(0, FileReader::SeekSet); file.Read(&hdr, sizeof(hdr)); return new FPCXTexture(lumpnum, hdr); } //========================================================================== // // // //========================================================================== FPCXTexture::FPCXTexture(int lumpnum, PCXHeader & hdr) : FWorldTexture(NULL, lumpnum) { bMasked = false; Width = LittleShort(hdr.xmax) - LittleShort(hdr.xmin) + 1; Height = LittleShort(hdr.ymax) - LittleShort(hdr.ymin) + 1; CalcBitSize(); } //========================================================================== // // // //========================================================================== FTextureFormat FPCXTexture::GetFormat() { return TEX_RGB; } //========================================================================== // // // //========================================================================== void FPCXTexture::ReadPCX1bit (uint8_t *dst, FileReader & lump, PCXHeader *hdr) { int y, i, bytes; int rle_count = 0; uint8_t rle_value = 0; uint8_t * srcp = new uint8_t[lump.GetLength() - sizeof(PCXHeader)]; lump.Read(srcp, lump.GetLength() - sizeof(PCXHeader)); uint8_t * src = srcp; for (y = 0; y < Height; ++y) { uint8_t * ptr = &dst[y * Width]; bytes = hdr->bytesPerScanLine; while (bytes--) { if (rle_count == 0) { if ( (rle_value = *src++) < 0xc0) { rle_count = 1; } else { rle_count = rle_value - 0xc0; rle_value = *src++; } } rle_count--; for (i = 7; i >= 0; --i, ptr ++) { // This can overflow for the last byte if not checked. if (ptr < dst+Width*Height) *ptr = ((rle_value & (1 << i)) > 0); } } } delete [] srcp; } //========================================================================== // // // //========================================================================== void FPCXTexture::ReadPCX4bits (uint8_t *dst, FileReader & lump, PCXHeader *hdr) { int rle_count = 0, rle_value = 0; int x, y, c; int bytes; uint8_t * line = new uint8_t[hdr->bytesPerScanLine]; uint8_t * colorIndex = new uint8_t[Width]; uint8_t * srcp = new uint8_t[lump.GetLength() - sizeof(PCXHeader)]; lump.Read(srcp, lump.GetLength() - sizeof(PCXHeader)); uint8_t * src = srcp; for (y = 0; y < Height; ++y) { uint8_t * ptr = &dst[y * Width]; memset (ptr, 0, Width * sizeof (uint8_t)); for (c = 0; c < 4; ++c) { uint8_t * pLine = line; bytes = hdr->bytesPerScanLine; while (bytes--) { if (rle_count == 0) { if ( (rle_value = *src++) < 0xc0) { rle_count = 1; } else { rle_count = rle_value - 0xc0; rle_value = *src++; } } rle_count--; *(pLine++) = rle_value; } } /* compute line's color indexes */ for (x = 0; x < Width; ++x) { if (line[x / 8] & (128 >> (x % 8))) ptr[x] += (1 << c); } } /* release memory */ delete [] colorIndex; delete [] line; delete [] srcp; } //========================================================================== // // // //========================================================================== void FPCXTexture::ReadPCX8bits (uint8_t *dst, FileReader & lump, PCXHeader *hdr) { int rle_count = 0, rle_value = 0; int y, bytes; uint8_t * srcp = new uint8_t[lump.GetLength() - sizeof(PCXHeader)]; lump.Read(srcp, lump.GetLength() - sizeof(PCXHeader)); uint8_t * src = srcp; for (y = 0; y < Height; ++y) { uint8_t * ptr = &dst[y * Width]; bytes = hdr->bytesPerScanLine; while (bytes--) { if (rle_count == 0) { if( (rle_value = *src++) < 0xc0) { rle_count = 1; } else { rle_count = rle_value - 0xc0; rle_value = *src++; } } rle_count--; *ptr++ = rle_value; } } delete [] srcp; } //========================================================================== // // // //========================================================================== void FPCXTexture::ReadPCX24bits (uint8_t *dst, FileReader & lump, PCXHeader *hdr, int planes) { int rle_count = 0, rle_value = 0; int y, c; int bytes; uint8_t * srcp = new uint8_t[lump.GetLength() - sizeof(PCXHeader)]; lump.Read(srcp, lump.GetLength() - sizeof(PCXHeader)); uint8_t * src = srcp; for (y = 0; y < Height; ++y) { /* for each color plane */ for (c = 0; c < planes; ++c) { uint8_t * ptr = &dst[y * Width * planes]; bytes = hdr->bytesPerScanLine; while (bytes--) { if (rle_count == 0) { if( (rle_value = *src++) < 0xc0) { rle_count = 1; } else { rle_count = rle_value - 0xc0; rle_value = *src++; } } rle_count--; ptr[c] = (uint8_t)rle_value; ptr += planes; } } } delete [] srcp; } //========================================================================== // // // //========================================================================== uint8_t *FPCXTexture::MakeTexture(FRenderStyle style) { uint8_t PaletteMap[256]; PCXHeader header; int bitcount; bool alphatex = !!(style.Flags & STYLEF_RedIsAlpha); auto lump = Wads.OpenLumpReader(SourceLump); lump.Read(&header, sizeof(header)); bitcount = header.bitsPerPixel * header.numColorPlanes; auto Pixels = new uint8_t[Width*Height]; if (bitcount < 24) { if (bitcount < 8) { switch (bitcount) { default: case 1: PaletteMap[0] = alphatex? 0 : GrayMap[0]; PaletteMap[1] = alphatex? 255 : GrayMap[255]; ReadPCX1bit (Pixels, lump, &header); break; case 4: for (int i = 0; i < 16; i++) { PaletteMap[i] = RGBToPalettePrecise(alphatex, header.palette[i * 3], header.palette[i * 3 + 1], header.palette[i * 3 + 2]); } ReadPCX4bits (Pixels, 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] = RGBToPalettePrecise(alphatex, r, g, b); } lump.Seek(sizeof(header), FileReader::SeekSet); ReadPCX8bits (Pixels, lump, &header); } if (Width == Height) { FlipSquareBlockRemap(Pixels, Width, Height, PaletteMap); } else { uint8_t *newpix = new uint8_t[Width*Height]; FlipNonSquareBlockRemap (newpix, Pixels, Width, Height, Width, PaletteMap); uint8_t *oldpix = Pixels; Pixels = newpix; delete[] oldpix; } } else { uint8_t * buffer = new uint8_t[Width*Height * 3]; uint8_t * row = buffer; ReadPCX24bits (buffer, lump, &header, 3); for(int y=0; yCopyPixelData(x, y, Pixels, Width, Height, 1, Width, rotate, pe, inf); } else { Pixels = new uint8_t[Width*Height * 3]; ReadPCX24bits (Pixels, lump, &header, 3); bmp->CopyPixelDataRGB(x, y, Pixels, Width, Height, 3, Width*3, rotate, CF_RGB, inf); } delete [] Pixels; return 0; } //=========================================================================== // // //=========================================================================== bool FPCXTexture::UseBasePalette() { return false; }