Serious-Engine/Sources/Engine/Graphics/ImageInfo.cpp

598 lines
18 KiB
C++

/* Copyright (c) 2002-2012 Croteam Ltd.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
#include "Engine/StdH.h"
#include <Engine/Graphics/ImageInfo.h>
#include <Engine/Graphics/Color.h>
#include <Engine/Base/Stream.h>
#include <Engine/Base/Memory.h>
#include <Engine/Math/Functions.h>
extern void FlipBitmap( UBYTE *pubSrc, UBYTE *pubDst, PIX pixWidth, PIX pixHeight, INDEX iFlipType, BOOL bAlphaChannel);
// Order of CroTeam true color pixel components
#define COMPONENT_1 red
#define COMPONENT_2 green
#define COMPONENT_3 blue
#define COMPONENT_4 alpha
// and vice versa
#define RED_COMPONENT 0
#define GREEN_COMPONENT 1
#define BLUE_COMPONENT 2
#define ALPHA_COMPONENT 3
// PCX header structure
struct PCXHeader
{
SBYTE MagicID;
SBYTE Version;
SBYTE Encoding;
SBYTE PixelBits;
SWORD Xmin, Ymin, Xmax, Ymax;
SWORD Hres, Vres;
UBYTE Palette[16*3];
SBYTE Reserved;
SBYTE Planes;
UWORD BytesPerLine;
SWORD PaletteInfo;
SWORD HscreenSize, VscreenSize;
SBYTE Filler[54];
};
static __forceinline CTStream &operator>>(CTStream &strm, PCXHeader &t) {
strm>>t.MagicID;
strm>>t.Version;
strm>>t.Encoding;
strm>>t.PixelBits;
strm>>t.Xmin;
strm>>t.Ymin;
strm>>t.Xmax;
strm>>t.Ymax;
strm>>t.Hres;
strm>>t.Vres;
strm.Read_t(t.Palette, sizeof (t.Palette));
strm>>t.Reserved;
strm>>t.Planes;
strm>>t.BytesPerLine;
strm>>t.PaletteInfo;
strm>>t.HscreenSize;
strm>>t.VscreenSize;
strm.Read_t(t.Filler, sizeof (t.Filler));
return strm;
}
static __forceinline CTStream &operator<<(CTStream &strm, const PCXHeader &t) {
strm<<t.MagicID;
strm<<t.Version;
strm<<t.Encoding;
strm<<t.PixelBits;
strm<<t.Xmin;
strm<<t.Ymin;
strm<<t.Xmax;
strm<<t.Ymax;
strm<<t.Hres;
strm<<t.Vres;
strm.Write_t(t.Palette, sizeof (t.Palette));
strm<<t.Reserved;
strm<<t.Planes;
strm<<t.BytesPerLine;
strm<<t.PaletteInfo;
strm<<t.HscreenSize;
strm<<t.VscreenSize;
strm.Write_t(t.Filler, sizeof (t.Filler));
return strm;
}
// TARGA header structure
struct TGAHeader
{
UBYTE IdLength;
UBYTE ColorMapType;
UBYTE ImageType;
UBYTE ColorMapSpec[5];
UWORD Xorigin;
UWORD Yorigin;
UWORD Width;
UWORD Height;
UBYTE BitsPerPixel;
UBYTE Descriptor;
};
static __forceinline CTStream &operator>>(CTStream &strm, TGAHeader &t) {
strm>>t.IdLength;
strm>>t.ColorMapType;
strm>>t.ImageType;
strm>>t.ColorMapSpec[5];
strm>>t.Xorigin;
strm>>t.Yorigin;
strm>>t.Width;
strm>>t.Height;
strm>>t.BitsPerPixel;
strm>>t.Descriptor;
return(strm);
}
static __forceinline CTStream &operator<<(CTStream &strm, const TGAHeader &t) {
strm<<t.IdLength;
strm<<t.ColorMapType;
strm<<t.ImageType;
strm<<t.ColorMapSpec[5];
strm<<t.Xorigin;
strm<<t.Yorigin;
strm<<t.Width;
strm<<t.Height;
strm<<t.BitsPerPixel;
strm<<t.Descriptor;
return(strm);
}
/******************************************************
* Routines for manipulating CroTeam picture raw format
*/
CImageInfo::CImageInfo() {
Detach();
}
CImageInfo::~CImageInfo() {
Clear();
}
// reads image info raw format from file
void CImageInfo::Read_t( CTStream *inFile) // throw char *
{
Clear();
// read image info header
inFile->ExpectID_t( CChunkID("CTII"));
if( inFile->GetSize_t() != 5*4) throw( "Invalid image info file.");
SLONG tmp;
*inFile >> tmp;
ii_Width = (PIX) tmp;
*inFile >> tmp;
ii_Height = (PIX) tmp;
*inFile >> tmp;
ii_BitsPerPixel = (SLONG) tmp;
// read image contents (all channels)
ULONG pic_size = ii_Width*ii_Height * ii_BitsPerPixel/8;
ii_Picture = (UBYTE*)AllocMemory( pic_size);
inFile->ReadFullChunk_t( CChunkID("IPIC"), ii_Picture, pic_size);
#if PLATFORM_BIGENDIAN
STUBBED("Byte order");
#endif
}
// writes image info raw format to file
void CImageInfo::Write_t( CTStream *outFile) const // throw char *
{
// write image info header
outFile->WriteID_t( CChunkID("CTII"));
outFile->WriteSize_t( 5*4);
*outFile << (PIX)ii_Width;
*outFile << (PIX)ii_Height;
*outFile << (SLONG)ii_BitsPerPixel;
// write image contents (all channels)
ULONG pic_size = ii_Width*ii_Height * ii_BitsPerPixel/8;
outFile->WriteFullChunk_t( CChunkID("IPIC"), ii_Picture, pic_size);
#if PLATFORM_BIGENDIAN
STUBBED("Byte order");
#endif
}
// initializes structure members and attaches pointer to image
void CImageInfo::Attach( UBYTE *pPicture, PIX pixWidth, PIX pixHeight, SLONG slBitsPerPixel)
{
// parameters must be meaningful
ASSERT( (pPicture != NULL) && (pixWidth>0) && (pixHeight>0));
ASSERT( (slBitsPerPixel == 24) || (slBitsPerPixel == 32));
// do it ...
ii_Picture = pPicture;
ii_Width = pixWidth;
ii_Height = pixHeight;
ii_BitsPerPixel = slBitsPerPixel;
}
// clears the content of an image info structure but does not free allocated memory
void CImageInfo::Detach(void)
{
ii_Picture = NULL;
ii_Width = 0;
ii_Height = 0;
ii_BitsPerPixel = 0;
}
// clears the content of an image info structure and frees allocated memory (if any)
void CImageInfo::Clear()
{
// if allocated, release picture memory
if( ii_Picture != NULL) FreeMemory( ii_Picture);
Detach();
}
// expand image edges
void CImageInfo::ExpandEdges( INDEX ctPasses/*=8192*/)
{
// do nothing if image is too small or doesn't have an alpha channel
if( ii_Width<3 || ii_Height<3 || ii_BitsPerPixel!=32) return;
// allocate some memory for spare picture and wipe it clean
SLONG slSize = ii_Width*ii_Height*ii_BitsPerPixel/8;
ULONG *pulSrc = (ULONG*)ii_Picture;
ULONG *pulDst = (ULONG*)AllocMemory(slSize);
memcpy( pulDst, pulSrc, slSize);
// loop while there are some more pixels to be processed or for specified number of passes
for( INDEX iPass=0; iPass<ctPasses; iPass++)
{
BOOL bAllPixelsVisible = TRUE;
// loop thru rows
for( PIX pixV=1; pixV<ii_Height-1; pixV++)
{ // loop thru pixels in row
for( PIX pixU=1; pixU<ii_Width-1; pixU++)
{ // determine pixel location
const PIX pixOffset = pixV*ii_Width + pixU;
// do nothing if it is already visible
COLOR col = ByteSwap(pulSrc[pixOffset]);
if( ((col&CT_AMASK)>>CT_ASHIFT)>3) continue;
bAllPixelsVisible = FALSE;
// average all surrounding pixels that are visible
ULONG ulRa=0, ulGa=0, ulBa=0;
INDEX ctVisible=0;
for( INDEX j=-1; j<=1; j++) {
for( INDEX i=-1; i<=1; i++) {
const PIX pixSurrOffset = pixOffset + j*ii_Width + i;
col = ByteSwap(pulSrc[pixSurrOffset]);
if( ((col&CT_AMASK)>>CT_ASHIFT)<4) continue; // skip non-visible pixels
UBYTE ubR, ubG, ubB;
ColorToRGB( col, ubR,ubG,ubB);
ulRa+=ubR; ulGa+=ubG; ulBa += ubB;
ctVisible++;
}
} // if there were some visible pixels around
if( ctVisible>0) {
// calc average
ulRa/=ctVisible; ulGa/=ctVisible; ulBa/=ctVisible;
col = RGBAToColor( ulRa,ulGa,ulBa,255);
// put it to center pixel
pulDst[pixOffset] = ByteSwap(col);
}
}
} // copy resulting picture over source
memcpy( pulSrc, pulDst, slSize);
// done if all pixels are visible
if( bAllPixelsVisible) break;
}
// free temp memory
FreeMemory(pulDst);
}
// sets image info structure members with info form file of any supported graphic format
// (CT RAW, PCX8, PCX24, TGA32 uncompressed), but does not load picture content nor palette
INDEX CImageInfo::GetGfxFileInfo_t( const CTFileName &strFileName) // throw char *
{
CTFileStream GfxFile;
TGAHeader TGAhdr;
PCXHeader PCXhdr;
// lets assume it's a TGA file
GfxFile.Open_t( strFileName, CTStream::OM_READ);
GfxFile>>TGAhdr;
GfxFile.Close();
// check for supported targa format
if( (TGAhdr.ImageType==2 || TGAhdr.ImageType==10) && TGAhdr.BitsPerPixel>=24) {
// targa it is, so clear image info and set new values
Clear();
ii_Width = TGAhdr.Width;
ii_Height = TGAhdr.Height;
ii_BitsPerPixel = TGAhdr.BitsPerPixel;
// we done here, no need to check further
return TGA_FILE;
}
// we miss Targa, so lets check for supported PCX format
GfxFile.Open_t( strFileName, CTStream::OM_READ);
GfxFile>>PCXhdr;
GfxFile.Close();
// check for supported PCX format
if( (PCXhdr.MagicID == 10) && (PCXhdr.PixelBits == 8)) {
// PCX it is, so clear image info and set new values
Clear();
ii_Width = PCXhdr.Xmax - PCXhdr.Xmin + 1;
ii_Height = PCXhdr.Ymax - PCXhdr.Ymin + 1;
ii_BitsPerPixel = PCXhdr.PixelBits * PCXhdr.Planes;
// we done here, no need to check further
return PCX_FILE;
}
// we didn't found a supported gfx format, sorry ...
return UNSUPPORTED_FILE;
}
/* TGA *********************************************************************************
* Routines that load and save true color (24 or 32 bit per pixel) uncompressed targa file
*/
void CImageInfo::LoadTGA_t( const CTFileName &strFileName) // throw char *
{
TGAHeader *pTGAHdr;
UBYTE *pTGABuffer, *pTGAImage;
SLONG slFileSize;
CTFileStream TGAFile;
Clear();
// determine file size
TGAFile.Open_t( strFileName, CTStream::OM_READ);
slFileSize = TGAFile.GetStreamSize();
// load entire TGA file to memory, as is, and close it (no further usage)
pTGABuffer = (UBYTE*)AllocMemory( slFileSize);
STUBBED("Byte swapping TGA data");
TGAFile.Read_t( pTGABuffer, slFileSize);
TGAFile.Close();
// TGA header starts at the begining of the TGA file
pTGAHdr = (struct TGAHeader*)pTGABuffer;
// TGA image bytes definition follows up
pTGAImage = pTGABuffer + sizeof(struct TGAHeader) + pTGAHdr->IdLength;
// detremine picture size dimensions
ii_Width = (SLONG)pTGAHdr->Width;
ii_Height = (SLONG)pTGAHdr->Height;
ii_BitsPerPixel = (SLONG)pTGAHdr->BitsPerPixel;
SLONG slBytesPerPixel = ii_BitsPerPixel/8;
PIX pixBitmapSize = ii_Width*ii_Height;
BOOL bAlphaChannel = (slBytesPerPixel==4);
// check for supported file types
ASSERT( slBytesPerPixel==3 || slBytesPerPixel==4);
if( slBytesPerPixel!=3 && slBytesPerPixel!=4) throw( TRANS("Unsupported BitsPerPixel in TGA format."));
// allocate memory for image content
ii_Picture = (UBYTE*)AllocMemory( ii_Width*ii_Height *slBytesPerPixel);
UBYTE *pubSrc = pTGAImage; // need 'walking' pointers
UBYTE *pubDst = ii_Picture;
// determine TGA image type
if( pTGAHdr->ImageType==10) {
// RLE encoded
UBYTE ubControl;
INDEX iBlockSize;
BOOL bRepeat;
PIX pixCurrentSize=0;
// loop thru blocks
while( pixCurrentSize<pixBitmapSize)
{ // readout control byte
ubControl = *pubSrc++;
bRepeat = ubControl&0x80;
iBlockSize = (ubControl&0x7F) +1;
// repeat or copy color values
for( INDEX i=0; i<iBlockSize; i++) {
*pubDst++ = pubSrc[0];
*pubDst++ = pubSrc[1];
*pubDst++ = pubSrc[2];
if( bAlphaChannel) *pubDst++ = pubSrc[3];
if( !bRepeat) pubSrc += slBytesPerPixel;
}
// advance for next block if repeated
if( bRepeat) pubSrc += slBytesPerPixel;
// update image size
pixCurrentSize += iBlockSize;
}
// mark that image was encoded to ImageInfo buffer
pTGAImage = ii_Picture;
}
// not true-colored?
else if( pTGAHdr->ImageType!=2) {
// whoops!
ASSERTALWAYS("Unsupported TGA format.");
throw( TRANS("Unsupported TGA format."));
}
// determine image flipping
INDEX iFlipType;
switch( (pTGAHdr->Descriptor&0x30)>>4) {
case 0: iFlipType = 1; break; // vertical flipping
case 1: iFlipType = 3; break; // diagonal flipping
case 3: iFlipType = 2; break; // horizontal flipping
default: iFlipType = 0; break; // no flipping (just copying)
}
// do flipping
FlipBitmap( pTGAImage, ii_Picture, ii_Width, ii_Height, iFlipType, bAlphaChannel);
// convert TGA pixel format to CroTeam
pubSrc = ii_Picture; // need 'walking' pointer again
for( INDEX iPix=0; iPix<pixBitmapSize; iPix++)
{ // flip bytes
Swap( pubSrc[0], pubSrc[2]); // R & B channels
pubSrc += slBytesPerPixel;
}
// free temorary allocated memory for TGA image format
FreeMemory( pTGABuffer);
}
// save TGA routine
void CImageInfo::SaveTGA_t( const CTFileName &strFileName) const // throw char *
{
TGAHeader *pTGAHdr;
UBYTE *pTGABuffer, *pTGAImage;
SLONG slFileSize;
PIX pixBitmapSize = ii_Width*ii_Height;
CTFileStream TGAFile;
// determine and check image info format
SLONG slBytesPerPixel = ii_BitsPerPixel/8;
ASSERT( slBytesPerPixel==3 || slBytesPerPixel==4);
if( slBytesPerPixel!=3 && slBytesPerPixel!=4) throw( TRANS( "Unsupported BitsPerPixel in ImageInfo header."));
// determine TGA file size and allocate memory
slFileSize = sizeof(struct TGAHeader) + pixBitmapSize *slBytesPerPixel;
pTGABuffer = (UBYTE*)AllocMemory( slFileSize);
pTGAHdr = (struct TGAHeader*)pTGABuffer;
pTGAImage = pTGABuffer + sizeof(struct TGAHeader);
// set TGA picture size dimensions
memset( pTGABuffer, 0x0, sizeof(struct TGAHeader));
pTGAHdr->Width = (UWORD)ii_Width;
pTGAHdr->Height = (UWORD)ii_Height;
pTGAHdr->BitsPerPixel = (UBYTE)ii_BitsPerPixel;
pTGAHdr->ImageType = 2;
// flip image vertically
BOOL bAlphaChannel = (slBytesPerPixel==4);
FlipBitmap( ii_Picture, pTGAImage, ii_Width, ii_Height, 1, bAlphaChannel);
// convert CroTeam's pixel format to TGA format
UBYTE *pubTmp = pTGAImage; // need 'walking' pointer
for( INDEX iPix=0; iPix<pixBitmapSize; iPix++)
{ // flip bytes
Swap( pubTmp[0], pubTmp[2]); // R & B channels
pubTmp += slBytesPerPixel;
}
// save entire TGA memory to file and close it
TGAFile.Create_t( strFileName);
TGAFile.Write_t( pTGABuffer, slFileSize);
TGAFile.Close();
// free temorary allocated memory for TGA image format
FreeMemory( pTGABuffer);
}
/* PCX ***********************************************************************
* This routine reads file with given file name and if it is valid PCX file it
* loads it into given ImageInfo structure in CroTeam true-color format.
* (and, if the one exists, loads the palette)
*/
void CImageInfo::LoadPCX_t( const CTFileName &strFileName) // throw char *
{
PCXHeader *pPCXHdr;
UBYTE *pPCXBuffer, *pPCXImage, *pPCXDecodedImage, *pTmp;
UBYTE data, counter;
SLONG pic_size, PCX_size, slFileSize;
CTFileStream PCXFile;
Clear();
// inconvinent way to determine file size
PCXFile.Open_t( strFileName, CTStream::OM_READ);
slFileSize = PCXFile.GetStreamSize();
// load entire PCX file to memory, as is, and close it (no further usage)
pPCXBuffer = (UBYTE*)AllocMemory( slFileSize);
STUBBED("Byte swapping PCX data");
PCXFile.Read_t( pPCXBuffer, slFileSize);
PCXFile.Close();
// PCX header starts at the begining of the PCX file
pPCXHdr = (struct PCXHeader*)pPCXBuffer;
// PCX image bytes definition follows up
pPCXImage = pPCXBuffer + sizeof( struct PCXHeader);
// detremine picture size dimensions
ii_Width = (SLONG)(pPCXHdr->Xmax - pPCXHdr->Xmin +1);
ii_Height = (SLONG)(pPCXHdr->Ymax - pPCXHdr->Ymin +1);
ii_BitsPerPixel = (SLONG)pPCXHdr->Planes*8;
pic_size = ii_Width * ii_Height * ii_BitsPerPixel/8;
// allocate memory for image content
ii_Picture = (UBYTE*)AllocMemory( pic_size);
// allocate memory for decoded PCX file that hasn't been converted to CT RAW Image format
PCX_size = (SLONG)(pPCXHdr->BytesPerLine * ii_Height * ii_BitsPerPixel/8);
pPCXDecodedImage = (UBYTE*)AllocMemory( PCX_size);
pTmp = pPCXDecodedImage; // save pointer for latter usage
// decode PCX file
for( INDEX i=0; i<PCX_size; ) // i is incremented by counter value at the and of the loop
{
// read one byte from PCX image in memory
data = *pPCXImage++;
// check byte-run mark
if( (data & 0xC0) == 0xC0) {
counter = data & 0x3F; // determine repeat value
data = *pPCXImage++; // read repeated data
// put several bytes of PCX image to decoded image area in memory
for( INDEX j=0; j<counter; j++)
*pPCXDecodedImage++ = data;
} else {
// put just one byte from PCX image to decoded image area in memory
counter = 1;
*pPCXDecodedImage++ = data;
}
// increment encoded image counter
i += counter;
}
pPCXDecodedImage = pTmp; // reset pointer
// convert decoded PCX image to CroTeam RAW Image Info format
SLONG slBytesPerPixel = ii_BitsPerPixel/8;
for( INDEX y=0; y<ii_Height; y++)
{
SLONG slYSrcOfs = y * ii_Width * slBytesPerPixel;
SLONG slYDstOfs = y * pPCXHdr->BytesPerLine * slBytesPerPixel;
// channel looper
for( INDEX p=0; p<slBytesPerPixel; p++)
{
SLONG slPOffset = p * pPCXHdr->BytesPerLine;
// byte looper
for( INDEX x=0; x<ii_Width; x++)
*(ii_Picture + slYSrcOfs + x*slBytesPerPixel + p) =
*(pPCXDecodedImage + slYDstOfs + slPOffset + x);
}
}
// free temorary allocated memory for PCX encoded and decoded image
FreeMemory( pPCXBuffer);
FreeMemory( pPCXDecodedImage);
}
// check for the supported gfx format file and invokes corresponding routine to load it
void CImageInfo::LoadAnyGfxFormat_t( const CTFileName &strFileName) // throw char *
{
INDEX iFileFormat = GetGfxFileInfo_t( strFileName);
if( iFileFormat == PCX_FILE) LoadPCX_t( strFileName);
if( iFileFormat == TGA_FILE) LoadTGA_t( strFileName);
if( iFileFormat == UNSUPPORTED_FILE) throw( "Gfx format not supported.");
}