From d9d7d0ea0dc629c60435360ea9f78b9478c24aca Mon Sep 17 00:00:00 2001 From: Evgeniy Meshcheryakov Date: Thu, 3 Oct 2024 11:55:58 +0300 Subject: [PATCH] Map editor: Transparency fix --- mapeditor/Animation.cpp | 118 ++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 66 deletions(-) diff --git a/mapeditor/Animation.cpp b/mapeditor/Animation.cpp index 0b1c89161..265dbf394 100644 --- a/mapeditor/Animation.cpp +++ b/mapeditor/Animation.cpp @@ -46,7 +46,7 @@ private: std::map > offset; std::unique_ptr data; - std::unique_ptr> palette; + QVector palette; public: DefFile(std::string Name); @@ -138,6 +138,44 @@ enum class DefType : uint32_t static FileCache animationCache; +//First 8 colors in def palette used for transparency +static constexpr std::array TARGET_PALETTE = +{ + qRgba(0, 0, 0, 0 ), // transparency ( used in most images ) + qRgba(0, 0, 0, 64 ), // shadow border ( used in battle, adventure map def's ) + qRgba(0, 0, 0, 64 ), // shadow border ( used in fog-of-war def's ) + qRgba(0, 0, 0, 128), // shadow body ( used in fog-of-war def's ) + qRgba(0, 0, 0, 128), // shadow body ( used in battle, adventure map def's ) + qRgba(0, 0, 0, 0 ), // selection / owner flag ( used in battle, adventure map def's ) + qRgba(0, 0, 0, 128), // shadow body below selection ( used in battle def's ) + qRgba(0, 0, 0, 64 ) // shadow border below selection ( used in battle def's ) +}; + +static constexpr std::array SOURCE_PALETTE = +{ + qRgba(0, 255, 255, 255), + qRgba(255, 150, 255, 255), + qRgba(255, 100, 255, 255), + qRgba(255, 50, 255, 255), + qRgba(255, 0, 255, 255), + qRgba(255, 255, 0, 255), + qRgba(180, 0, 255, 255), + qRgba(0, 255, 0, 255) +}; + +static bool colorsSimilar(const QRgb & lhs, const QRgb & rhs) +{ + // it seems that H3 does not requires exact match to replace colors -> (255, 103, 255) gets interpreted as shadow + // exact logic is not clear and requires extensive testing with image editing + // potential reason is that H3 uses 16-bit color format (565 RGB bits), meaning that 3 least significant bits are lost in red and blue component + static const int threshold = 8; + int diffR = qRed(lhs) - qRed(rhs); + int diffG = qGreen(lhs) - qGreen(rhs); + int diffB = qBlue(lhs) - qBlue(rhs); + int diffA = qAlpha(lhs) - qAlpha(rhs); + return std::abs(diffR) < threshold && std::abs(diffG) < threshold && std::abs(diffB) < threshold && std::abs(diffA) < threshold; +}; + /************************************************************************* * DefFile, class used for def loading * *************************************************************************/ @@ -160,24 +198,12 @@ DefFile::DefFile(std::string Name): }; #endif // 0 - //First 8 colors in def palette used for transparency - constexpr std::array H3Palette = - { - qRgba(0, 0, 0, 0), // 100% - transparency - qRgba(0, 0, 0, 32), // 75% - shadow border, - qRgba(0, 0, 0, 64), // TODO: find exact value - qRgba(0, 0, 0, 128), // TODO: for transparency - qRgba(0, 0, 0, 128), // 50% - shadow body - qRgba(0, 0, 0, 0), // 100% - selection highlight - qRgba(0, 0, 0, 128), // 50% - shadow body below selection - qRgba(0, 0, 0, 64) // 75% - shadow border below selection - }; data = animationCache.getCachedFile(AnimationPath::builtin("SPRITES/" + Name)); - palette = std::make_unique>(256); + palette = QVector(256); int it = 0; - ui32 type = read_le_u32(data.get() + it); + //ui32 type = read_le_u32(data.get() + it); it += 4; //int width = read_le_u32(data + it); it+=4;//not used //int height = read_le_u32(data + it); it+=4; @@ -191,59 +217,19 @@ DefFile::DefFile(std::string Name): c[0] = data[it++]; c[1] = data[it++]; c[2] = data[it++]; - (*palette)[i] = qRgba(c[0], c[1], c[2], 255); + palette[i] = qRgba(c[0], c[1], c[2], 255); } - switch(static_cast(type)) + // these colors seems to be used unconditionally + palette[0] = TARGET_PALETTE[0]; + palette[1] = TARGET_PALETTE[1]; + palette[4] = TARGET_PALETTE[4]; + + // rest of special colors are used only if their RGB values are close to H3 + for (uint32_t i = 0; i < 8; ++i) { - case DefType::SPELL: - (*palette)[0] = H3Palette[0]; - break; - case DefType::SPRITE: - case DefType::SPRITE_FRAME: - for(ui32 i= 0; i<8; i++) - (*palette)[i] = H3Palette[i]; - break; - case DefType::CREATURE: - (*palette)[0] = H3Palette[0]; - (*palette)[1] = H3Palette[1]; - (*palette)[4] = H3Palette[4]; - (*palette)[5] = H3Palette[5]; - (*palette)[6] = H3Palette[6]; - (*palette)[7] = H3Palette[7]; - break; - case DefType::MAP: - case DefType::MAP_HERO: - (*palette)[0] = H3Palette[0]; - (*palette)[1] = H3Palette[1]; - (*palette)[4] = H3Palette[4]; - //5 = owner flag, handled separately - break; - case DefType::TERRAIN: - (*palette)[0] = H3Palette[0]; - (*palette)[1] = H3Palette[1]; - (*palette)[2] = H3Palette[2]; - (*palette)[3] = H3Palette[3]; - (*palette)[4] = H3Palette[4]; - break; - case DefType::CURSOR: - (*palette)[0] = H3Palette[0]; - break; - case DefType::INTERFACE: - (*palette)[0] = H3Palette[0]; - (*palette)[1] = H3Palette[1]; - (*palette)[4] = H3Palette[4]; - //player colors handled separately - //TODO: disallow colorizing other def types - break; - case DefType::BATTLE_HERO: - (*palette)[0] = H3Palette[0]; - (*palette)[1] = H3Palette[1]; - (*palette)[4] = H3Palette[4]; - break; - default: - logAnim->error("Unknown def type %d in %s", type, Name); - break; + if (colorsSimilar(SOURCE_PALETTE[i], palette[i])) + palette[i] = TARGET_PALETTE[i]; } @@ -421,7 +407,7 @@ std::shared_ptr DefFile::loadFrame(size_t frame, size_t group) const } - img->setColorTable(*palette); + img->setColorTable(palette); return img; }