/* ** tgatexture.cpp ** Texture class for TGA images ** **--------------------------------------------------------------------------- ** Copyright 2006 Christoph Oelckers ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** ** */ #include "doomtype.h" #include "files.h" #include "r_local.h" #include "w_wad.h" #include "templates.h" #include "bitmap.h" #include "colormatcher.h" //========================================================================== // // TGA file header // //========================================================================== #pragma pack(1) struct TGAHeader { BYTE id_len; BYTE has_cm; BYTE img_type; SWORD cm_first; SWORD cm_length; BYTE cm_size; SWORD x_origin; SWORD y_origin; SWORD width; SWORD height; BYTE bpp; BYTE img_desc; }; #pragma pack() //========================================================================== // // a TGA texture // //========================================================================== class FTGATexture : public FTexture { public: FTGATexture (int lumpnum, TGAHeader *); ~FTGATexture (); const BYTE *GetColumn (unsigned int column, const Span **spans_out); const BYTE *GetPixels (); void Unload (); FTextureFormat GetFormat (); int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf = NULL); bool UseBasePalette(); protected: BYTE *Pixels; Span **Spans; void ReadCompressed(FileReader &lump, BYTE * buffer, int bytesperpixel); virtual void MakeTexture (); friend class FTexture; }; //========================================================================== // // // //========================================================================== FTexture *TGATexture_TryCreate(FileReader & file, int lumpnum) { TGAHeader hdr; if (file.GetLength() < (long)sizeof(hdr)) return NULL; file.Seek(0, SEEK_SET); file.Read(&hdr, sizeof(hdr)); hdr.width = LittleShort(hdr.width); hdr.height = LittleShort(hdr.height); // Not much that can be done here because TGA does not have a proper // header to be identified with. if (hdr.has_cm != 0 && hdr.has_cm != 1) return NULL; if (hdr.width <=0 || hdr.height <=0 || hdr.width > 2048 || hdr.height > 2048) return NULL; if (hdr.bpp != 8 && hdr.bpp != 15 && hdr.bpp != 16 && hdr.bpp !=24 && hdr.bpp !=32) return NULL; if (hdr.img_type <= 0 || hdr.img_type > 11) return NULL; if (hdr.img_type >=4 && hdr.img_type <= 8) return NULL; if ((hdr.img_desc & 16) != 0) return NULL; file.Seek(0, SEEK_SET); file.Read(&hdr, sizeof(hdr)); hdr.width = LittleShort(hdr.width); hdr.height = LittleShort(hdr.height); return new FTGATexture(lumpnum, &hdr); } //========================================================================== // // // //========================================================================== FTGATexture::FTGATexture (int lumpnum, TGAHeader * hdr) : FTexture(NULL, lumpnum), Pixels(0), Spans(0) { Wads.GetLumpName (Name, lumpnum); Name[8] = 0; Width = hdr->width; Height = hdr->height; // Alpha channel is used only for 32 bit RGBA and paletted images with RGBA palettes. bMasked = (hdr->img_desc&15)==8 && (hdr->bpp==32 || (hdr->img_type==1 && hdr->cm_size==32)); CalcBitSize(); } //========================================================================== // // // //========================================================================== FTGATexture::~FTGATexture () { Unload (); if (Spans != NULL) { FreeSpans (Spans); Spans = NULL; } } //========================================================================== // // // //========================================================================== void FTGATexture::Unload () { if (Pixels != NULL) { delete[] Pixels; Pixels = NULL; } } //========================================================================== // // // //========================================================================== FTextureFormat FTGATexture::GetFormat() { return TEX_RGB; } //========================================================================== // // // //========================================================================== const BYTE *FTGATexture::GetColumn (unsigned int column, const Span **spans_out) { if (Pixels == NULL) { MakeTexture (); } if ((unsigned)column >= (unsigned)Width) { if (WidthMask + 1 == Width) { column &= WidthMask; } else { column %= Width; } } if (spans_out != NULL) { if (Spans == NULL) { Spans = CreateSpans (Pixels); } *spans_out = Spans[column]; } return Pixels + column*Height; } //========================================================================== // // // //========================================================================== const BYTE *FTGATexture::GetPixels () { if (Pixels == NULL) { MakeTexture (); } return Pixels; } //========================================================================== // // // //========================================================================== void FTGATexture::ReadCompressed(FileReader &lump, BYTE * buffer, int bytesperpixel) { BYTE b; BYTE data[4]; int Size = Width * Height; while (Size > 0) { lump >> b; if (b & 128) { b&=~128; lump.Read(data, bytesperpixel); for (int i=MIN(Size, (b+1)); i>0; i--) { buffer[0] = data[0]; if (bytesperpixel>=2) buffer[1] = data[1]; if (bytesperpixel>=3) buffer[2] = data[2]; if (bytesperpixel==4) buffer[3] = data[3]; buffer+=bytesperpixel; } } else { lump.Read(buffer, MIN(Size, (b+1))*bytesperpixel); buffer += (b+1)*bytesperpixel; } Size -= b+1; } } //========================================================================== // // // //========================================================================== void FTGATexture::MakeTexture () { BYTE PaletteMap[256]; FWadLump lump = Wads.OpenLumpNum (SourceLump); TGAHeader hdr; WORD w; BYTE r,g,b,a; BYTE * buffer; Pixels = new BYTE[Width*Height]; lump.Read(&hdr, sizeof(hdr)); lump.Seek(hdr.id_len, SEEK_CUR); 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: lump >> w; r = (w & 0x001F) << 3; g = (w & 0x03E0) >> 2; b = (w & 0x7C00) >> 7; a = 255; break; case 24: lump >> b >> g >> r; a=255; break; case 32: lump >> b >> g >> r >> a; if ((hdr.img_desc&15)!=8) a=255; break; default: // should never happen r=g=b=a=0; break; } PaletteMap[i] = a>=128? ColorMatcher.Pick(r, g, b) : 0; } } int Size = Width * Height * (hdr.bpp>>3); buffer = new BYTE[Size]; if (hdr.img_type < 4) // uncompressed { lump.Read(buffer, Size); } else // compressed { ReadCompressed(lump, buffer, hdr.bpp>>3); } BYTE * 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][(v>>5) & 0x1f][v & 0x1f]; p+=step_x; } } break; case 24: for(int y=0;y>3][p[1]>>3][p[0]>>3]; p+=step_x; } } break; case 32: if ((hdr.img_desc&15)!=8) // 32 bits without a valid alpha channel { for(int y=0;y>3][p[1]>>3][p[0]>>3]; p+=step_x; } } } else { for(int y=0;y= 128? RGB32k[p[2]>>3][p[1]>>3][p[0]>>3] : 0; p+=step_x; } } } break; default: break; } break; case 3: // Grayscale switch (hdr.bpp) { case 8: for(int y=0;y> w; r = (w & 0x001F) << 3; g = (w & 0x03E0) >> 2; b = (w & 0x7C00) >> 7; a = 255; break; case 24: lump >> b >> g >> r; a=255; break; case 32: lump >> b >> g >> r >> a; if ((hdr.img_desc&15)!=8) a=255; else if (a!=0 && a!=255) transval = true; break; default: // should never happen r=g=b=a=0; break; } pe[i] = PalEntry(a, r, g, b); } } int Size = Width * Height * (hdr.bpp>>3); sbuffer = new BYTE[Size]; if (hdr.img_type < 4) // uncompressed { lump.Read(sbuffer, Size); } else // compressed { ReadCompressed(lump, sbuffer, hdr.bpp>>3); } BYTE * ptr = sbuffer; int step_x = (hdr.bpp>>3); int Pitch = Width * step_x; /* if (hdr.img_desc&32) { ptr += (Width-1) * step_x; step_x =- step_x; } */ if (!(hdr.img_desc&32)) { ptr += (Height-1) * Pitch; Pitch = -Pitch; } switch (hdr.img_type & 7) { case 1: // paletted bmp->CopyPixelData(x, y, ptr, Width, Height, step_x, Pitch, rotate, pe, inf); break; case 2: // RGB switch (hdr.bpp) { case 15: case 16: bmp->CopyPixelDataRGB(x, y, ptr, Width, Height, step_x, Pitch, rotate, CF_RGB555, inf); break; case 24: bmp->CopyPixelDataRGB(x, y, ptr, Width, Height, step_x, Pitch, rotate, CF_BGR, inf); break; case 32: if ((hdr.img_desc&15)!=8) // 32 bits without a valid alpha channel { bmp->CopyPixelDataRGB(x, y, ptr, Width, Height, step_x, Pitch, rotate, CF_BGR, inf); } else { bmp->CopyPixelDataRGB(x, y, ptr, Width, Height, step_x, Pitch, rotate, CF_BGRA, inf); transval = -1; } break; default: break; } break; case 3: // Grayscale switch (hdr.bpp) { case 8: for(int i=0;i<256;i++) pe[i]=PalEntry(255,i,i,i); // gray map bmp->CopyPixelData(x, y, ptr, Width, Height, step_x, Pitch, rotate, pe, inf); break; case 16: bmp->CopyPixelDataRGB(x, y, ptr, Width, Height, step_x, Pitch, rotate, CF_I16, inf); break; default: break; } break; default: break; } delete [] sbuffer; return transval; } //=========================================================================== // // //=========================================================================== bool FTGATexture::UseBasePalette() { return false; }