#include "IconLoader.h" #include "DisplayDeviceManager.h" #include "PLCore.h" #include "PLCTabReducer.h" #include "PLStandardColors.h" #include "ResourceManager.h" #include "QDStandardPalette.h" #include "QDPixMap.h" #include "SharedTypes.h" // Color icons (cicn) format: // ColorIconSerializedData (82 bytes) // Mask bits // B&W bits // BEColorTableHeader (8 bytes) // BEColorTableItem (8 bytes) // Color bits // ICON format is just a 32x32 bitfield struct IconImagePrefix { BEUInt32_t m_unknown; // Seems to always be zero BEUInt16_t m_pitch; // +0x8000 for color }; struct ColorIconSerializedData { IconImagePrefix m_colorPrefix; BEPixMap m_colorPixMap; IconImagePrefix m_maskPrefix; BEBitMap m_maskBitMap; IconImagePrefix m_bwPrefix; BEBitMap m_bwBitMap; uint8_t m_unused[4]; }; namespace PortabilityLayer { class IconLoaderImpl final : public IconLoader { public: bool LoadColorIcon(const int16_t id, THandle &outColorImage, THandle &outBWImage, THandle &outMaskImage) override; static IconLoaderImpl *GetInstance(); private: static bool ParseColorImage(const IconImagePrefix &prefix, const BEPixMap &pixMapHeader, THandle &outHandle, const uint8_t *&dataPtr); static bool ParseBWImage(const IconImagePrefix &prefix, const BEBitMap &bitMapHeader, THandle &outHandle, const uint8_t *&dataPtr); static IconLoaderImpl ms_instance; }; bool IconLoaderImpl::LoadColorIcon(const int16_t id, THandle &outColorImage, THandle &outBWImage, THandle &outMaskImage) { THandle data = PortabilityLayer::ResourceManager::GetInstance()->GetAppResource('cicn', id).StaticCast(); if (!data) return false; GP_STATIC_ASSERT(sizeof(ColorIconSerializedData) == 82); ColorIconSerializedData header; memcpy(&header, *data, sizeof(header)); const uint8_t *dataPtr = (*data) + sizeof(header); THandle maskImage; if (!ParseBWImage(header.m_maskPrefix, header.m_maskBitMap, maskImage, dataPtr)) { data.Dispose(); return false; } THandle bwImage; if (!ParseBWImage(header.m_bwPrefix, header.m_bwBitMap, bwImage, dataPtr)) { PixMapImpl::Destroy(maskImage); data.Dispose(); return false; } THandle colorImage; if (!ParseColorImage(header.m_colorPrefix, header.m_colorPixMap, colorImage, dataPtr)) { PixMapImpl::Destroy(bwImage); PixMapImpl::Destroy(maskImage); data.Dispose(); return false; } outColorImage = colorImage; outBWImage = bwImage; outMaskImage = maskImage; return true; } bool IconLoaderImpl::ParseColorImage(const IconImagePrefix &prefix, const BEPixMap &pixMapHeader, THandle &outHandle, const uint8_t *&dataPtr) { if (pixMapHeader.m_componentCount != 1 || pixMapHeader.m_packType != 0) { PL_NotYetImplemented(); return false; } const uint8_t *inData = dataPtr; BEColorTableHeader colorTableHeader; memcpy(&colorTableHeader, inData, sizeof(BEColorTableHeader)); inData += sizeof(BEColorTableHeader); const size_t numItems = static_cast(colorTableHeader.m_numItemsMinusOne) + 1; if (numItems > 256) return false; uint8_t remapping[256]; PortabilityLayer::RGBAColor palette[256]; for (int i = 0; i < 256; i++) { remapping[i] = 0; palette[i] = StdColors::Black(); } for (size_t i = 0; i < numItems; i++) { BEColorTableItem ctabItem; memcpy(&ctabItem, inData, sizeof(BEColorTableItem)); inData += sizeof(BEColorTableItem); const uint16_t index = ctabItem.m_index; if (index >= 256) return false; const PortabilityLayer::RGBAColor remappedColor = CTabReducer::DecodeClutItem(ctabItem); palette[i] = remappedColor; remapping[index] = StandardPalette::GetInstance()->MapColorLUT(remappedColor); } const Rect rect = pixMapHeader.m_bounds.ToRect(); if (!rect.IsValid()) return false; GpPixelFormat_t pixelFormat = DisplayDeviceManager::GetInstance()->GetPixelFormat(); THandle pixMap = PixMapImpl::Create(rect, pixelFormat); if (!pixMap) return false; const size_t width = rect.Width(); const size_t height = rect.Height(); const size_t inPitch = (prefix.m_pitch & 0x7fff); uint8_t *outData = static_cast((*pixMap)->GetPixelData()); const size_t outPitch = (*pixMap)->GetPitch(); if (pixelFormat == GpPixelFormats::k8BitStandard) { for (size_t row = 0; row < height; row++) { if (numItems > 16) { // 8bpp for (size_t col = 0; col < width; col++) { const unsigned int index = inData[col]; outData[col] = remapping[index]; } } else if (numItems > 4) { // 4bpp for (size_t col = 0; col < width; col++) { const unsigned int index = (inData[col / 2] >> (4 - ((col & 1) * 4))) & 0x0f; outData[col] = remapping[index]; } } else if (numItems > 2) { // 2bpp for (size_t col = 0; col < width; col++) { const unsigned int index = (inData[col / 4] >> (6 - ((col & 3) * 2))) & 0x03; outData[col] = remapping[index]; } } else { // 1bpp for (size_t col = 0; col < width; col++) { const unsigned int index = (inData[col / 4] >> (7 - (col & 7))) & 0x01; outData[col] = remapping[index]; } } inData += inPitch; outData += outPitch; } } else if (pixelFormat == GpPixelFormats::kRGB32) { for (size_t row = 0; row < height; row++) { if (numItems > 16) { // 8bpp for (size_t col = 0; col < width; col++) { const unsigned int index = inData[col]; const PortabilityLayer::RGBAColor &color = palette[index]; outData[col * 4 + 0] = color.r; outData[col * 4 + 1] = color.g; outData[col * 4 + 2] = color.b; outData[col * 4 + 3] = 255; } } else if (numItems > 4) { // 4bpp for (size_t col = 0; col < width; col++) { const unsigned int index = (inData[col / 2] >> (4 - ((col & 1) * 4))) & 0x0f; const PortabilityLayer::RGBAColor &color = palette[index]; outData[col * 4 + 0] = color.r; outData[col * 4 + 1] = color.g; outData[col * 4 + 2] = color.b; outData[col * 4 + 3] = 255; } } else if (numItems > 2) { // 2bpp for (size_t col = 0; col < width; col++) { const unsigned int index = (inData[col / 4] >> (6 - ((col & 3) * 2))) & 0x03; const PortabilityLayer::RGBAColor &color = palette[index]; outData[col * 4 + 0] = color.r; outData[col * 4 + 1] = color.g; outData[col * 4 + 2] = color.b; outData[col * 4 + 3] = 255; } } else { // 1bpp for (size_t col = 0; col < width; col++) { const unsigned int index = (inData[col / 4] >> (7 - (col & 7))) & 0x01; const PortabilityLayer::RGBAColor &color = palette[index]; outData[col * 4 + 0] = color.r; outData[col * 4 + 1] = color.g; outData[col * 4 + 2] = color.b; outData[col * 4 + 3] = 255; } } inData += inPitch; outData += outPitch; } } else { PL_NotYetImplemented(); } outHandle = pixMap; dataPtr = inData; return true; } bool IconLoaderImpl::ParseBWImage(const IconImagePrefix &prefix, const BEBitMap &bitMapHeader, THandle &outHandle, const uint8_t *&dataPtr) { const Rect rect = bitMapHeader.m_bounds.ToRect(); if (!rect.IsValid()) return false; THandle pixMap = PixMapImpl::Create(rect, GpPixelFormats::kBW1); if (!pixMap) return THandle(); const size_t inPitch = prefix.m_pitch; const size_t height = rect.Height(); const size_t width = rect.Width(); const uint8_t *inData = dataPtr; uint8_t *outData = static_cast((*pixMap)->GetPixelData()); const size_t outPitch = (*pixMap)->GetPitch(); for (size_t row = 0; row < height; row++) { for (size_t col = 0; col < width; col++) { if (inData[col / 8] & (0x80 >> (col & 7))) outData[col] = 0xff; else outData[col] = 0x00; } inData += inPitch; outData += outPitch; } dataPtr = inData; outHandle = pixMap; return true; } IconLoaderImpl *IconLoaderImpl::GetInstance() { return &ms_instance; } IconLoaderImpl IconLoaderImpl::ms_instance; IconLoader *IconLoader::GetInstance() { return IconLoaderImpl::GetInstance(); } }