#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 // 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<= (255 & ~((1<= (255 & ~((1<= (255 & ~((1<= 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(1<(1<(1<(1<(1<(1< 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(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(pCmap)->nEntries; db_logf4(("\tfound a %u colour %scompressed IFF body chunk",nThisPaletteSize,static_cast(pHdr)->eCompression ? "" : "un")); pThis->m_listBodyChunks.add_entry(static_cast(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 itChunks(&m_listBodyChunks); !itChunks.done(); itChunks.next()) { IFF::IlbmCmapChunk * pCmap = static_cast(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(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<nEntries]; // copy the palette for (unsigned i=0; inEntries; ++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(m_pPalette[AwIffConvTransp::iTranspCol].r)>>pixelFormat.redRightShift<(m_pPalette[AwIffConvTransp::iTranspCol].g)>>pixelFormat.greenRightShift<(m_pPalette[AwIffConvTransp::iTranspCol].b)>>pixelFormat.blueRightShift<(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(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::Do(pDest, nDestWidth, pSrc.uintP+nSrcOffset, nSrcWidth); } else { switch (m_pHdr->eMasking) { case IFF::IlbmBmhdChunk::MASK_NONE: GenericConvertRow::Do(pDest, nDestWidth, pSrc.uintP+nSrcOffset, nSrcWidth, pPalette db_code1(DB_COMMA nPaletteSize)); break; case IFF::IlbmBmhdChunk::MASK_TRANSPARENTCOL: GenericConvertRow::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)