avp/src/win95/awiffld.cpp
Steven Fuller 4db15e7459 More compiling. Things are starting to get hacky (adding stubs, commenting
out unimplemented windows stuff, and so on).
2019-08-20 02:22:36 +02:00

502 lines
15 KiB
C++

#include "fixer.h"
#ifndef DB_LEVEL
#define DB_LEVEL 4
#endif
#include "db.h"
#ifndef DB_COMMA
#define DB_COMMA ,
#endif
#include "awtexld.hpp"
#include "iff.hpp"
#include "iff_ilbm.hpp"
#include "list_tem.hpp"
#include <limits.h>
// conversion functors for IFF loader
class AwIffConvNull
{
public:
static inline unsigned DoConv (unsigned const * _colP, AwTl::Colour const * db_code1(DB_COMMA unsigned))
{
db_assert1(AwTl::pixelFormat.palettizedB);
return *_colP;
}
};
class AwIffConvNonTransp : public AwTl::Colour::ConvNonTransp
{
public:
static inline unsigned DoConv(unsigned const * pCol, AwTl::Colour const * pPalette db_code1(DB_COMMA unsigned nPaletteSize))
{
db_assert1(pPalette);
db_onlyassert1(*pCol < nPaletteSize);
return AwTl::Colour::ConvNonTransp::DoConv(&pPalette[*pCol]);
}
};
class AwIffConvTransp
{
public:
static unsigned iTranspCol; // the index of the transparent colour
static unsigned rawTranspCol; // the value of a transparent pixel on the surface
static inline unsigned DoConv(unsigned const * pCol, AwTl::Colour const * pPalette db_code1(DB_COMMA unsigned nPaletteSize))
{
using namespace AwTl;
if (*pCol == iTranspCol) return rawTranspCol;
unsigned rv = AwIffConvNonTransp::DoConv(pCol,pPalette db_code1(DB_COMMA nPaletteSize));
if (rv != rawTranspCol) return rv;
// make the colour non-transparent (nb: only an occasional case)
// OK, Here's the plan:
// First, suppose that I were to decrease either the Red, Green or Blue
// component in order to make the colour non-transparent.
// Taking Red as an example, I'll work out what the resultant red value
// will be if I decrease red to achieve a non-transparent colour
// I'll compare this to the actual red value of the colour in question
// The lower the difference, the better it'll be (ie. closer to the
// original colour).
// Obviously, I'll do the same for Green and Blue
// Then I'll repeat the process but this time considering increasing
// the components.
// I'll then have six values which are scores (the lower the better)
// for what colour component to change and how to change it.
// If any of the components cannot be decreased (ie. their resulting
// value is already zero) or increased (ie. their resulting value
// is at maximum), then I'll set the corresponding score to
// UINT_MAX so that that colour-changing operation won't be selected
// (because that'll be the one that buggers everything up).
unsigned nRedDiffDown =
(pPalette[*pCol].r < 1<<pixelFormat.redRightShift ) ? UINT_MAX
: (pPalette[*pCol].r & (1<<pixelFormat.redRightShift )-1) + ((1<<pixelFormat.redRightShift )+1)/2;
unsigned nGreenDiffDown =
(pPalette[*pCol].g < 1<<pixelFormat.greenRightShift) ? UINT_MAX
: (pPalette[*pCol].g & (1<<pixelFormat.greenRightShift)-1) + ((1<<pixelFormat.greenRightShift)+1)/2;
unsigned nBlueDiffDown =
(pPalette[*pCol].b < 1<<pixelFormat.blueRightShift ) ? UINT_MAX
: (pPalette[*pCol].b & (1<<pixelFormat.blueRightShift )-1) + ((1<<pixelFormat.blueRightShift )+1)/2;
unsigned nRedDiffUp =
(pPalette[*pCol].r >= (255 & ~((1<<pixelFormat.redRightShift )-1) ) ? UINT_MAX
: (1<<pixelFormat.redRightShift )*3/2 - (pPalette[*pCol].r & (1<<pixelFormat.redRightShift )-1));
unsigned nGreenDiffUp =
(pPalette[*pCol].g >= (255 & ~((1<<pixelFormat.greenRightShift)-1) ) ? UINT_MAX
: (1<<pixelFormat.greenRightShift)*3/2 - (pPalette[*pCol].g & (1<<pixelFormat.greenRightShift)-1));
unsigned nBlueDiffUp =
(pPalette[*pCol].b >= (255 & ~((1<<pixelFormat.blueRightShift )-1) ) ? UINT_MAX
: (1<<pixelFormat.blueRightShift )*3/2 - (pPalette[*pCol].b & (1<<pixelFormat.blueRightShift )-1));
// Pick lowest value and do the business
Colour colAdj = pPalette[*pCol];
#if defined(_MSC_VER) && _MSC_VER >= 1100
// VC5.0 gives inane warnings when += type operators
// are used on types smaller than int (even with
// explicit casting!)
#pragma warning(disable:4244)
#endif
if
(
nBlueDiffUp <= nBlueDiffDown
&& nBlueDiffUp <= nRedDiffUp
&& nBlueDiffUp <= nRedDiffDown
&& nBlueDiffUp <= nGreenDiffUp
&& nBlueDiffUp <= nGreenDiffDown
)
{
colAdj.b += static_cast<unsigned char>(1<<pixelFormat.blueRightShift);
}
else if
(
nBlueDiffDown <= nRedDiffUp
&& nBlueDiffDown <= nRedDiffDown
&& nBlueDiffDown <= nGreenDiffUp
&& nBlueDiffDown <= nGreenDiffDown
)
{
colAdj.b -= static_cast<unsigned char>(1<<pixelFormat.blueRightShift);
}
else if
(
nRedDiffUp <= nRedDiffDown
&& nRedDiffUp <= nGreenDiffUp
&& nRedDiffUp <= nGreenDiffDown
)
{
colAdj.r += static_cast<unsigned char>(1<<pixelFormat.redRightShift);
}
else if
(
nRedDiffDown <= nGreenDiffUp
&& nRedDiffDown <= nGreenDiffDown
)
{
colAdj.r -= static_cast<unsigned char>(1<<pixelFormat.redRightShift);
}
else if (nGreenDiffUp <= nGreenDiffDown)
{
colAdj.g += static_cast<unsigned char>(1<<pixelFormat.greenRightShift);
}
else
{
colAdj.g -= static_cast<unsigned char>(1<<pixelFormat.greenRightShift);
}
#if defined(_MSC_VER) && _MSC_VER == 1100
// VC5.0 gives inane warnings when += type operators
// are used on types smaller than int (even with
// explicit casting!)
#pragma warning(default:4244)
#endif
return Colour::ConvNonTransp::DoConv(&colAdj);
}
};
unsigned AwIffConvTransp::iTranspCol;
unsigned AwIffConvTransp::rawTranspCol;
// IFF Loader
class AwIffLoader : public AwTl::TexFileLoader
{
public:
AwIffLoader() : m_pPalette(NULL), m_bDecoding(false) {}
protected:
virtual ~AwIffLoader();
virtual void LoadHeaderInfo(MediaMedium * pMedium);
virtual unsigned GetNumColours();
virtual unsigned GetMinPaletteSize();
virtual bool HasTransparentMask(bool bDefault);
virtual void AllocateBuffers(bool bWantBackup, unsigned nMaxPaletteSize);
virtual void OnBeginRestoring(unsigned nMaxPaletteSize);
virtual AwTl::Colour * GetPalette();
virtual AwTl::PtrUnion GetRowPtr(unsigned nRow);
virtual void LoadNextRow(AwTl::PtrUnion pRow);
virtual void ConvertRow(AwTl::PtrUnion pDest, unsigned nDestWidth, AwTl::PtrUnionConst pSrc, unsigned nSrcOffset, unsigned nSrcWidth, AwTl::Colour * pPalette db_code1(DB_COMMA unsigned nPaletteSize));
virtual DWORD GetTransparentColour();
virtual void OnFinishLoading(bool bSuccess);
virtual void OnFinishRestoring(bool bSuccess);
virtual AwBackupTexture * CreateBackupTexture();
private:
static bool Enumerator(IFF::Chunk * pChunk, void * pData);
// list of chunks found by enumerator
List<IFF::IlbmBodyChunk *> m_listBodyChunks;
// smallest and largest palette sizes of versions of this image
unsigned m_nMaxPaletteSize;
unsigned m_nMinPaletteSize;
// buffer for palette -
// since the IFF cmap table is in a different format to what the Aw loaders require
// (maybe should think about standardizing the data types?)
AwTl::Colour * m_pPalette;
// iff data
IFF::File m_ifData;
IFF::IlbmBmhdChunk * m_pHdr;
IFF::IlbmCmapChunk * m_pCmap;
IFF::IlbmBodyChunk * m_pBody;
bool m_bDecoding;
};
AwIffLoader::~AwIffLoader()
{
if (m_pPalette) delete[] m_pPalette;
}
void AwIffLoader::LoadHeaderInfo(MediaMedium * pMedium)
{
db_log4("\tLoading an IFF file");
while (m_listBodyChunks.size())
m_listBodyChunks.delete_first_entry();
if (!m_ifData.Load(pMedium) || !m_ifData.GetContents())
{
// if (NO_ERROR == (awTlLastWinErr = GetLastError()))
// awTlLastErr = AW_TLE_BADFILEDATA;
// else
// awTlLastErr = AW_TLE_CANTREADFILE;
db_log3("AwCreateTexture() [AwIffLoader::LoadHeaderInfo] : ERROR: IFF file load failed");
}
else
{
m_nMinPaletteSize = UINT_MAX;
m_nMaxPaletteSize = 0;
m_ifData.GetContents()->EnumChildren("ILBM","BODY",Enumerator,this);
}
}
unsigned AwIffLoader::GetNumColours()
{
return m_nMaxPaletteSize;
}
unsigned AwIffLoader::GetMinPaletteSize()
{
return m_nMinPaletteSize;
}
void AwIffLoader::AllocateBuffers(bool /*bWantBackup*/, unsigned nMaxPaletteSize)
{
// we will need to allocate buffers when restoring as well as first-time loading
// so allocate buffers in OnBeginRestoring() which we'll call here
OnBeginRestoring(nMaxPaletteSize);
}
bool AwIffLoader::Enumerator(IFF::Chunk * pChunk, void * pData)
{
db_assert1(pChunk);
db_assert1(pData);
AwIffLoader * pThis = static_cast<AwIffLoader *>(pData);
IFF::Chunk * pCmap = pChunk->GetProperty("CMAP");
IFF::Chunk * pHdr = pChunk->GetProperty("BMHD");
if (pCmap && pHdr) // must have these two properties
{
unsigned nThisPaletteSize = static_cast<IFF::IlbmCmapChunk *>(pCmap)->nEntries;
db_logf4(("\tfound a %u colour %scompressed IFF body chunk",nThisPaletteSize,static_cast<IFF::IlbmBmhdChunk *>(pHdr)->eCompression ? "" : "un"));
pThis->m_listBodyChunks.add_entry(static_cast<IFF::IlbmBodyChunk *>(pChunk));
if (nThisPaletteSize < pThis->m_nMinPaletteSize)
pThis->m_nMinPaletteSize = nThisPaletteSize;
if (nThisPaletteSize > pThis->m_nMaxPaletteSize)
pThis->m_nMaxPaletteSize = nThisPaletteSize;
}
else db_log3("AwCreateTexture(): WARNING: IFF body chunk found with insufficient associated property chunks");
return true; // continue enumeration
}
void AwIffLoader::OnBeginRestoring(unsigned nMaxPaletteSize)
{
using namespace AwTl;
if (m_listBodyChunks.size())
{
// if decodeing, m_pBody will be valid
if (m_bDecoding)
{
m_pBody->EndDecode();
m_bDecoding = false;
}
m_pBody = NULL;
unsigned nBestPaletteSize = 0;
for (LIF<IFF::IlbmBodyChunk *> itChunks(&m_listBodyChunks); !itChunks.done(); itChunks.next())
{
IFF::IlbmCmapChunk * pCmap = static_cast<IFF::IlbmCmapChunk *>(itChunks()->GetProperty("CMAP"));
db_assert1(pCmap);
if ((!nMaxPaletteSize || pCmap->nEntries <= nMaxPaletteSize) && pCmap->nEntries > nBestPaletteSize)
{
m_pBody = itChunks();
m_pCmap = pCmap;
nBestPaletteSize = pCmap->nEntries;
}
}
if (m_pBody)
{
m_pHdr = static_cast<IFF::IlbmBmhdChunk *>(m_pBody->GetProperty("BMHD"));
// delete old buffers
if (m_pPalette) delete[] m_pPalette;
// allocate buffer for palette, make it extra big to cope with corrupt files
unsigned nAllocPaletteSize = m_pCmap->nEntries;
unsigned nAllocPaletteSizeShift = 0;
while (0!=(nAllocPaletteSize >>= 1))
{
++nAllocPaletteSizeShift;
}
m_pPalette = new Colour [2<<nAllocPaletteSizeShift]; // prevent corrupt data causing a crash
//m_pPalette = new Colour [m_pCmap->nEntries];
// copy the palette
for (unsigned i=0; i<m_pCmap->nEntries; ++i)
{
// hacked testa
m_pPalette[i].r = m_pCmap->pTable[i].r;
m_pPalette[i].g = m_pCmap->pTable[i].g;
m_pPalette[i].b = m_pCmap->pTable[i].b;
}
// set the width height and palette size in the base class
m_nPaletteSize = m_pCmap->nEntries;
m_nWidth = m_pHdr->width;
m_nHeight = m_pHdr->height;
// prepare to decode the data
m_pBody->BeginDecode();
m_bDecoding = true;
// set the transparent mask colours
switch (m_pHdr->eMasking)
{
case IFF::IlbmBmhdChunk::MASK_NONE:
break;
case IFF::IlbmBmhdChunk::MASK_TRANSPARENTCOL:
AwIffConvTransp::iTranspCol = m_pHdr->iTranspCol;
if (pixelFormat.palettizedB)
AwIffConvTransp::rawTranspCol = AwIffConvTransp::iTranspCol;
else
AwIffConvTransp::rawTranspCol =
static_cast<unsigned>(m_pPalette[AwIffConvTransp::iTranspCol].r)>>pixelFormat.redRightShift<<pixelFormat.redLeftShift
|static_cast<unsigned>(m_pPalette[AwIffConvTransp::iTranspCol].g)>>pixelFormat.greenRightShift<<pixelFormat.greenLeftShift
|static_cast<unsigned>(m_pPalette[AwIffConvTransp::iTranspCol].b)>>pixelFormat.blueRightShift<<pixelFormat.blueLeftShift;
break;
default:
db_log3("AwCreateTexture() [AwIffLoader::OnBeginRestoring] : ERROR: IFF mask field wrong");
// awTlLastErr = AW_TLE_BADFILEDATA;
}
}
else
{
// awTlLastErr = AW_TLE_CANTPALETTIZE; // no suitable chunk found
db_log3("AwCreateTexture() [AwIffLoader::OnBeginRestoring] : ERROR: No suitable IFF body chunk found");
}
}
else
{
// awTlLastErr = AW_TLE_BADFILEDATA;
db_log3("AwCreateTexture() [AwIffLoader::OnBeginRestoring] : ERROR: IFF file not loaded or contains no image data");
}
}
AwTl::Colour * AwIffLoader::GetPalette()
{
return m_pPalette;
}
bool AwIffLoader::HasTransparentMask(bool bDefault)
{
if (m_listBodyChunks.size())
{
IFF::IlbmBmhdChunk * pHdr = static_cast<IFF::IlbmBmhdChunk *>(m_listBodyChunks.first_entry()->GetProperty("BMHD"));
db_assert1(pHdr);
return (IFF::IlbmBmhdChunk::MASK_TRANSPARENTCOL == pHdr->eMasking);
}
else
return bDefault;
}
DWORD AwIffLoader::GetTransparentColour()
{
return AwIffConvTransp::rawTranspCol;
}
AwTl::PtrUnion AwIffLoader::GetRowPtr(unsigned /*nRow*/)
{
// the iff object has an internal buffer to which a pointer
// is returned when we decode each row
// unfortunately we have to cast constness away, but never mind
return const_cast<unsigned *>(m_pBody->DecodeNextRow());
}
void AwIffLoader::LoadNextRow(AwTl::PtrUnion /*pRow*/)
{
// GetRowPtr() has called DecodeNextRow()
// which has filled in the data already
// so do nothing here
}
void AwIffLoader::ConvertRow(AwTl::PtrUnion pDest, unsigned nDestWidth, AwTl::PtrUnionConst pSrc, unsigned nSrcOffset, unsigned nSrcWidth, AwTl::Colour * pPalette db_code1(DB_COMMA unsigned nPaletteSize))
{
using namespace AwTl;
// we have overridden this function for two reasons:
// 1. The data type for each texel in the row is unsigned int
// to allow for the fact that all images are palettized
// with no limit on the palette size. The default
// implementation would assume BYTE (unsigned char)
// 2. The transparency flag and colour is stored in the
// file and the transparency flag passed to AwCreateTexture()
// is ignored. The transparent colour does not have to be 0,0,0
// either
db_assert1(pPalette);
db_assert1(pPalette == m_pPalette);
// we can still use GenericConvertRow though, using the conversion functors above
if (pixelFormat.palettizedB)
{
GenericConvertRow<AwIffConvNull,unsigned>::Do(pDest, nDestWidth, pSrc.uintP+nSrcOffset, nSrcWidth);
}
else
{
switch (m_pHdr->eMasking)
{
case IFF::IlbmBmhdChunk::MASK_NONE:
GenericConvertRow<AwIffConvNonTransp,unsigned>::Do(pDest, nDestWidth, pSrc.uintP+nSrcOffset, nSrcWidth, pPalette db_code1(DB_COMMA nPaletteSize));
break;
case IFF::IlbmBmhdChunk::MASK_TRANSPARENTCOL:
GenericConvertRow<AwIffConvTransp,unsigned>::Do(pDest, nDestWidth, pSrc.uintP+nSrcOffset, nSrcWidth, pPalette db_code1(DB_COMMA nPaletteSize));
break;
default:
db_log3("AwCreateTexture() [AwIffLoader::ConvertRow] : ERROR: IFF mask field wrong");
// awTlLastErr = AW_TLE_BADFILEDATA;
}
}
}
void AwIffLoader::OnFinishLoading(bool /*bSuccess*/)
{
if (m_bDecoding)
{
m_pBody->EndDecode();
m_bDecoding = false;
}
}
void AwIffLoader::OnFinishRestoring(bool /*bSuccess*/)
{
if (m_bDecoding)
{
m_pBody->EndDecode();
m_bDecoding = false;
}
}
AwBackupTexture * AwIffLoader::CreateBackupTexture()
{
// use the same object for restoring
AddRef();
return this;
}
// Valid file ID fields: 'FORM' 'LIST' 'CAT ' - we can load them all
AWTEXLD_IMPLEMENT_DYNCREATE("FORM",AwIffLoader)
AWTEXLD_IMPLEMENT_DYNCREATE("LIST",AwIffLoader)
AWTEXLD_IMPLEMENT_DYNCREATE("CAT ",AwIffLoader)