summaryrefslogtreecommitdiff
path: root/3dc/win95/awIffLd.cpp
diff options
context:
space:
mode:
Diffstat (limited to '3dc/win95/awIffLd.cpp')
-rw-r--r--3dc/win95/awIffLd.cpp507
1 files changed, 507 insertions, 0 deletions
diff --git a/3dc/win95/awIffLd.cpp b/3dc/win95/awIffLd.cpp
new file mode 100644
index 0000000..de3be19
--- /dev/null
+++ b/3dc/win95/awIffLd.cpp
@@ -0,0 +1,507 @@
+#include "advwin32.h"
+#ifndef DB_LEVEL
+#define DB_LEVEL 4
+#endif
+#include "db.h"
+#ifndef DB_COMMA
+ #define DB_COMMA ,
+#endif
+
+#pragma warning(disable: 4701)
+#include "awTexLd.hpp"
+#pragma warning(default: 4701)
+
+#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(): 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(): ERROR: IFF mask field wrong");
+ awTlLastErr = AW_TLE_BADFILEDATA;
+ }
+ }
+ else
+ {
+ awTlLastErr = AW_TLE_CANTPALETTIZE; // no suitable chunk found
+ db_log3("AwCreateTexture(): ERROR: No suitable IFF body chunk found");
+ }
+ }
+ else
+ {
+ awTlLastErr = AW_TLE_BADFILEDATA;
+ db_log3("AwCreateTexture(): 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(): 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
+#ifdef _MSC_VER
+ // VC5.0 tries to compile out code that is in a library
+ // and it thinks isn't being used
+ #line 427
+#endif
+AWTEXLD_IMPLEMENT_DYNCREATE("FORM",AwIffLoader)
+AWTEXLD_IMPLEMENT_DYNCREATE("LIST",AwIffLoader)
+AWTEXLD_IMPLEMENT_DYNCREATE("CAT ",AwIffLoader)