diff options
Diffstat (limited to 'src/win95/awiffld.cpp')
| -rw-r--r-- | src/win95/awiffld.cpp | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/src/win95/awiffld.cpp b/src/win95/awiffld.cpp new file mode 100644 index 0000000..de3be19 --- /dev/null +++ b/src/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) |
