From 61a8c53bb6d7a9f6199ea223760124ea69750537 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 5 Jun 2023 23:51:18 +0300 Subject: [PATCH] Fix colors in def palette turning into transparency unconditionally --- client/render/CDefFile.cpp | 106 +++++++++++++++---------------------- 1 file changed, 42 insertions(+), 64 deletions(-) diff --git a/client/render/CDefFile.cpp b/client/render/CDefFile.cpp index 43d7f8d49..4ba165647 100644 --- a/client/render/CDefFile.cpp +++ b/client/render/CDefFile.cpp @@ -82,9 +82,19 @@ static CFileCache animationCache; * DefFile, class used for def loading * *************************************************************************/ -bool operator== (const SDL_Color & lhs, const SDL_Color & rhs) +static bool colorsSimilar (const SDL_Color & lhs, const SDL_Color & rhs) { - return (lhs.a == rhs.a) && (lhs.b == rhs.b) &&(lhs.g == rhs.g) &&(lhs.r == rhs.r); + // 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 = static_cast(lhs.r) - rhs.r; + int diffG = static_cast(lhs.g) - rhs.g; + int diffB = static_cast(lhs.b) - rhs.b; + int diffA = static_cast(lhs.a) - rhs.a; + + return std::abs(diffR) < threshold && std::abs(diffG) < threshold && std::abs(diffB) < threshold && std::abs(diffA) < threshold; } CDefFile::CDefFile(std::string Name): @@ -92,23 +102,34 @@ CDefFile::CDefFile(std::string Name): palette(nullptr) { //First 8 colors in def palette used for transparency - static SDL_Color H3Palette[8] = - { - { 0, 0, 0, 0},// transparency ( used in most images ) - { 0, 0, 0, 64},// shadow border ( used in battle, adventure map def's ) - { 0, 0, 0, 64},// shadow border ( used in fog-of-war def's ) - { 0, 0, 0, 128},// shadow body ( used in fog-of-war def's ) - { 0, 0, 0, 128},// shadow body ( used in battle, adventure map def's ) - { 0, 0, 0, 0},// selection ( used in battle def's ) - { 0, 0, 0, 128},// shadow body below selection ( used in battle def's ) - { 0, 0, 0, 64} // shadow border below selection ( used in battle def's ) + static const SDL_Color sourcePalette[8] = { + {0, 255, 255, SDL_ALPHA_OPAQUE}, + {255, 150, 255, SDL_ALPHA_OPAQUE}, + {255, 100, 255, SDL_ALPHA_OPAQUE}, + {255, 50, 255, SDL_ALPHA_OPAQUE}, + {255, 0, 255, SDL_ALPHA_OPAQUE}, + {255, 255, 0, SDL_ALPHA_OPAQUE}, + {180, 0, 255, SDL_ALPHA_OPAQUE}, + {0, 255, 0, SDL_ALPHA_OPAQUE} }; + + static const SDL_Color targetPalette[8] = { + {0, 0, 0, 0 }, // transparency ( used in most images ) + {0, 0, 0, 64 }, // shadow border ( used in battle, adventure map def's ) + {0, 0, 0, 64 }, // shadow border ( used in fog-of-war def's ) + {0, 0, 0, 128}, // shadow body ( used in fog-of-war def's ) + {0, 0, 0, 128}, // shadow body ( used in battle, adventure map def's ) + {0, 0, 0, 0 }, // selection / owner flag ( used in battle, adventure map def's ) + {0, 0, 0, 128}, // shadow body below selection ( used in battle def's ) + {0, 0, 0, 64 } // shadow border below selection ( used in battle def's ) + }; + data = animationCache.getCachedFile(ResourceID(std::string("SPRITES/") + Name, EResType::ANIMATION)); palette = std::unique_ptr(new SDL_Color[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; @@ -124,58 +145,15 @@ CDefFile::CDefFile(std::string Name): palette[i].a = SDL_ALPHA_OPAQUE; } - switch(static_cast(type)) - { - 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_HERO: - palette[0] = H3Palette[0]; - palette[1] = H3Palette[1]; - palette[4] = H3Palette[4]; - //5 = owner flag, handled separately - break; - case DefType::MAP: - 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; - } + // first color seems to be used unconditionally as 100% transparency + palette[0] = targetPalette[0]; + // rest of special colors are used only if their RGB values are close to H3 + for (uint32_t i = 1; i < 8; ++i) + { + if (colorsSimilar(sourcePalette[i], palette[i])) + palette[i] = targetPalette[i]; + } for (ui32 i=0; i