Procházet zdrojové kódy

More robust management of body/shadow/overlay split

Ivan Savenko před 11 měsíci
rodič
revize
251155d913

+ 1 - 1
client/ClientCommandManager.cpp

@@ -394,7 +394,7 @@ void ClientCommandManager::handleDef2bmpCommand(std::istringstream& singleWordBu
 {
 	std::string URI;
 	singleWordBuffer >> URI;
-	auto anim = GH.renderHandler().loadAnimation(AnimationPath::builtin(URI), EImageBlitMode::ALPHA);
+	auto anim = GH.renderHandler().loadAnimation(AnimationPath::builtin(URI), EImageBlitMode::SIMPLE);
 	anim->exportBitmaps(VCMIDirs::get().userExtractedPath());
 }
 

+ 1 - 1
client/battle/BattleAnimationClasses.cpp

@@ -883,7 +883,7 @@ uint32_t CastAnimation::getAttackClimaxFrame() const
 
 EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, int effects, bool reversed):
 	BattleAnimation(owner),
-	animation(GH.renderHandler().loadAnimation(animationName, EImageBlitMode::ALPHA)),
+	animation(GH.renderHandler().loadAnimation(animationName, EImageBlitMode::SIMPLE)),
 	effectFlags(effects),
 	effectFinished(false),
 	reversed(reversed)

+ 1 - 3
client/battle/BattleFieldController.cpp

@@ -114,7 +114,7 @@ BattleFieldController::BattleFieldController(BattleInterface & owner):
 
 	//preparing cells and hexes
 	cellBorder = GH.renderHandler().loadImage(ImagePath::builtin("CCELLGRD.BMP"), EImageBlitMode::COLORKEY);
-	cellShade = GH.renderHandler().loadImage(ImagePath::builtin("CCELLSHD.BMP"), EImageBlitMode::ALPHA);
+	cellShade = GH.renderHandler().loadImage(ImagePath::builtin("CCELLSHD.BMP"), EImageBlitMode::SIMPLE);
 	cellUnitMovementHighlight = GH.renderHandler().loadImage(ImagePath::builtin("UnitMovementHighlight.PNG"), EImageBlitMode::COLORKEY);
 	cellUnitMaxMovementHighlight = GH.renderHandler().loadImage(ImagePath::builtin("UnitMaxMovementHighlight.PNG"), EImageBlitMode::COLORKEY);
 
@@ -124,8 +124,6 @@ BattleFieldController::BattleFieldController(BattleInterface & owner):
 	rangedFullDamageLimitImages = GH.renderHandler().loadAnimation(AnimationPath::builtin("battle/rangeHighlights/rangeHighlightsGreen.json"), EImageBlitMode::COLORKEY);
 	shootingRangeLimitImages = GH.renderHandler().loadAnimation(AnimationPath::builtin("battle/rangeHighlights/rangeHighlightsRed.json"), EImageBlitMode::COLORKEY);
 
-	cellShade->setShadowEnabled(true);
-
 	if(!owner.siegeController)
 	{
 		auto bfieldType = owner.getBattle()->battleGetBattlefieldType();

+ 1 - 1
client/battle/BattleInterfaceClasses.cpp

@@ -398,7 +398,7 @@ BattleHero::BattleHero(const BattleInterface & owner, const CGHeroInstance * her
 	else
 		animationPath = hero->getHeroClass()->imageBattleMale;
 
-	animation = GH.renderHandler().loadAnimation(animationPath, EImageBlitMode::ALPHA);
+	animation = GH.renderHandler().loadAnimation(animationPath, EImageBlitMode::WITH_SHADOW);
 
 	pos.w = 64;
 	pos.h = 136;

+ 4 - 4
client/battle/BattleObstacleController.cpp

@@ -50,11 +50,11 @@ void BattleObstacleController::loadObstacleImage(const CObstacleInstance & oi)
 	if (oi.obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE)
 	{
 		// obstacle uses single bitmap image for animations
-		obstacleImages[oi.uniqueID] = GH.renderHandler().loadImage(animationName.toType<EResType::IMAGE>(), EImageBlitMode::COLORKEY);
+		obstacleImages[oi.uniqueID] = GH.renderHandler().loadImage(animationName.toType<EResType::IMAGE>(), EImageBlitMode::SIMPLE);
 	}
 	else
 	{
-		obstacleAnimations[oi.uniqueID] = GH.renderHandler().loadAnimation(animationName, EImageBlitMode::COLORKEY);
+		obstacleAnimations[oi.uniqueID] = GH.renderHandler().loadAnimation(animationName, EImageBlitMode::SIMPLE);
 		obstacleImages[oi.uniqueID] = obstacleAnimations[oi.uniqueID]->getImage(0);
 	}
 }
@@ -78,7 +78,7 @@ void BattleObstacleController::obstacleRemoved(const std::vector<ObstacleChanges
 		if(animationPath.empty())
 			continue;
 
-		auto animation = GH.renderHandler().loadAnimation(animationPath, EImageBlitMode::COLORKEY);
+		auto animation = GH.renderHandler().loadAnimation(animationPath, EImageBlitMode::SIMPLE);
 		auto first = animation->getImage(0, 0);
 		if(!first)
 			continue;
@@ -105,7 +105,7 @@ void BattleObstacleController::obstaclePlaced(const std::vector<std::shared_ptr<
 		if(!oi->visibleForSide(side, owner.getBattle()->battleHasNativeStack(side)))
 			continue;
 
-		auto animation = GH.renderHandler().loadAnimation(oi->getAppearAnimation(), EImageBlitMode::ALPHA);
+		auto animation = GH.renderHandler().loadAnimation(oi->getAppearAnimation(), EImageBlitMode::SIMPLE);
 		auto first = animation->getImage(0, 0);
 		if(!first)
 			continue;

+ 5 - 5
client/battle/CreatureAnimation.cpp

@@ -17,6 +17,7 @@
 #include "../render/CAnimation.h"
 #include "../render/Canvas.h"
 #include "../render/ColorFilter.h"
+#include "../render/Colors.h"
 #include "../render/IRenderHandler.h"
 
 static const ColorRGBA creatureBlueBorder = { 0, 255, 255, 255 };
@@ -199,8 +200,8 @@ CreatureAnimation::CreatureAnimation(const AnimationPath & name_, TSpeedControll
 	  speedController(controller),
 	  once(false)
 {
-	forward = GH.renderHandler().loadAnimation(name_, EImageBlitMode::ALPHA);
-	reverse = GH.renderHandler().loadAnimation(name_, EImageBlitMode::ALPHA);
+	forward = GH.renderHandler().loadAnimation(name_, EImageBlitMode::WITH_SHADOW_AND_OVERLAY);
+	reverse = GH.renderHandler().loadAnimation(name_, EImageBlitMode::WITH_SHADOW_AND_OVERLAY);
 
 	// if necessary, add one frame into vcmi-only group DEAD
 	if(forward->size(size_t(ECreatureAnimType::DEAD)) == 0)
@@ -339,15 +340,14 @@ void CreatureAnimation::nextFrame(Canvas & canvas, const ColorFilter & shifter,
 
 	if(image)
 	{
-		image->setShadowEnabled(true);
-		image->setOverlayEnabled(isIdle());
 		if (isIdle())
 			image->setOverlayColor(genBorderColor(getBorderStrength(elapsedTime), border));
+		else
+			image->setOverlayColor(Colors::TRANSPARENCY);
 
 		image->adjustPalette(shifter, 0);
 
 		canvas.draw(image, pos.topLeft(), Rect(0, 0, pos.w, pos.h));
-
 	}
 }
 

+ 15 - 22
client/mapView/MapRenderer.cpp

@@ -316,7 +316,7 @@ uint8_t MapRendererBorder::checksum(IMapRendererContext & context, const int3 &
 MapRendererFow::MapRendererFow()
 {
 	fogOfWarFullHide = GH.renderHandler().loadAnimation(AnimationPath::builtin("TSHRC"), EImageBlitMode::OPAQUE);
-	fogOfWarPartialHide = GH.renderHandler().loadAnimation(AnimationPath::builtin("TSHRE"), EImageBlitMode::ALPHA);
+	fogOfWarPartialHide = GH.renderHandler().loadAnimation(AnimationPath::builtin("TSHRE"), EImageBlitMode::SIMPLE);
 
 	static const std::vector<int> rotations = {22, 15, 2, 13, 12, 16, 28, 17, 20, 19, 7, 24, 26, 25, 30, 32, 27};
 
@@ -383,24 +383,25 @@ std::shared_ptr<CAnimation> MapRendererObjects::getBaseAnimation(const CGObjectI
 	}
 
 	bool generateMovementGroups = (info->id == Obj::BOAT) || (info->id == Obj::HERO);
+	bool enableOverlay = obj->ID != Obj::BOAT && obj->ID != Obj::HERO && obj->getOwner() != PlayerColor::UNFLAGGABLE;
 
 	// Boat appearance files only contain single, unanimated image
 	// proper boat animations are actually in different file
 	if (info->id == Obj::BOAT)
 		if(auto boat = dynamic_cast<const CGBoat*>(obj); boat && !boat->actualAnimation.empty())
-			return getAnimation(boat->actualAnimation, generateMovementGroups);
+			return getAnimation(boat->actualAnimation, generateMovementGroups, enableOverlay);
 
-	return getAnimation(info->animationFile, generateMovementGroups);
+	return getAnimation(info->animationFile, generateMovementGroups, enableOverlay);
 }
 
-std::shared_ptr<CAnimation> MapRendererObjects::getAnimation(const AnimationPath & filename, bool generateMovementGroups)
+std::shared_ptr<CAnimation> MapRendererObjects::getAnimation(const AnimationPath & filename, bool generateMovementGroups, bool enableOverlay)
 {
 	auto it = animations.find(filename);
 
 	if(it != animations.end())
 		return it->second;
 
-	auto ret = GH.renderHandler().loadAnimation(filename, EImageBlitMode::ALPHA);
+	auto ret = GH.renderHandler().loadAnimation(filename, enableOverlay ? EImageBlitMode::WITH_SHADOW_AND_OVERLAY : EImageBlitMode::WITH_SHADOW);
 	animations[filename] = ret;
 
 	if(generateMovementGroups)
@@ -427,14 +428,14 @@ std::shared_ptr<CAnimation> MapRendererObjects::getFlagAnimation(const CGObjectI
 	{
 		assert(dynamic_cast<const CGHeroInstance *>(obj) != nullptr);
 		assert(obj->tempOwner.isValidPlayer());
-		return getAnimation(AnimationPath::builtin(heroFlags[obj->tempOwner.getNum()]), true);
+		return getAnimation(AnimationPath::builtin(heroFlags[obj->tempOwner.getNum()]), true, false);
 	}
 
 	if(obj->ID == Obj::BOAT)
 	{
 		const auto * boat = dynamic_cast<const CGBoat *>(obj);
 		if(boat && boat->hero && !boat->flagAnimations[boat->hero->tempOwner.getNum()].empty())
-			return getAnimation(boat->flagAnimations[boat->hero->tempOwner.getNum()], true);
+			return getAnimation(boat->flagAnimations[boat->hero->tempOwner.getNum()], true, false);
 	}
 
 	return nullptr;
@@ -447,7 +448,7 @@ std::shared_ptr<CAnimation> MapRendererObjects::getOverlayAnimation(const CGObje
 		// Boats have additional animation with waves around boat
 		const auto * boat = dynamic_cast<const CGBoat *>(obj);
 		if(boat && boat->hero && !boat->overlayAnimation.empty())
-			return getAnimation(boat->overlayAnimation, true);
+			return getAnimation(boat->overlayAnimation, true, false);
 	}
 	return nullptr;
 }
@@ -478,22 +479,14 @@ void MapRendererObjects::renderImage(IMapRendererContext & context, Canvas & tar
 		return;
 
 	image->setAlpha(transparency);
-	image->setShadowEnabled(true);
-	if (object->ID != Obj::HERO)
+	if (object->ID != Obj::HERO) // heroes use separate image with flag instead of player-colored palette
 	{
-		image->setOverlayEnabled(object->getOwner().isValidPlayer() || object->getOwner() == PlayerColor::NEUTRAL);
-
 		if (object->getOwner().isValidPlayer())
 			image->setOverlayColor(graphics->playerColors[object->getOwner().getNum()]);
 
 		if (object->getOwner() == PlayerColor::NEUTRAL)
 			image->setOverlayColor(graphics->neutralColor);
 	}
-	else
-	{
-		// heroes use separate image with flag instead of player-colored palette
-		image->setOverlayEnabled(false);
-	}
 
 	Point offsetPixels = context.objectImageOffset(object->id, coordinates);
 
@@ -567,10 +560,10 @@ uint8_t MapRendererObjects::checksum(IMapRendererContext & context, const int3 &
 }
 
 MapRendererOverlay::MapRendererOverlay()
-	: imageGrid(GH.renderHandler().loadImage(ImagePath::builtin("debug/grid"), EImageBlitMode::ALPHA))
-	, imageBlocked(GH.renderHandler().loadImage(ImagePath::builtin("debug/blocked"), EImageBlitMode::ALPHA))
-	, imageVisitable(GH.renderHandler().loadImage(ImagePath::builtin("debug/visitable"), EImageBlitMode::ALPHA))
-	, imageSpellRange(GH.renderHandler().loadImage(ImagePath::builtin("debug/spellRange"), EImageBlitMode::ALPHA))
+	: imageGrid(GH.renderHandler().loadImage(ImagePath::builtin("debug/grid"), EImageBlitMode::COLORKEY))
+	, imageBlocked(GH.renderHandler().loadImage(ImagePath::builtin("debug/blocked"), EImageBlitMode::COLORKEY))
+	, imageVisitable(GH.renderHandler().loadImage(ImagePath::builtin("debug/visitable"), EImageBlitMode::COLORKEY))
+	, imageSpellRange(GH.renderHandler().loadImage(ImagePath::builtin("debug/spellRange"), EImageBlitMode::COLORKEY))
 {
 
 }
@@ -626,7 +619,7 @@ uint8_t MapRendererOverlay::checksum(IMapRendererContext & context, const int3 &
 }
 
 MapRendererPath::MapRendererPath()
-	: pathNodes(GH.renderHandler().loadAnimation(AnimationPath::builtin("ADAG"), EImageBlitMode::ALPHA))
+	: pathNodes(GH.renderHandler().loadAnimation(AnimationPath::builtin("ADAG"), EImageBlitMode::SIMPLE))
 {
 }
 

+ 1 - 1
client/mapView/MapRenderer.h

@@ -77,7 +77,7 @@ class MapRendererObjects
 	std::shared_ptr<CAnimation> getFlagAnimation(const CGObjectInstance * obj);
 	std::shared_ptr<CAnimation> getOverlayAnimation(const CGObjectInstance * obj);
 
-	std::shared_ptr<CAnimation> getAnimation(const AnimationPath & filename, bool generateMovementGroups);
+	std::shared_ptr<CAnimation> getAnimation(const AnimationPath & filename, bool generateMovementGroups, bool enableOverlay);
 
 	std::shared_ptr<IImage> getImage(IMapRendererContext & context, const CGObjectInstance * obj, const std::shared_ptr<CAnimation> & animation) const;
 

+ 23 - 6
client/render/IImage.h

@@ -37,9 +37,29 @@ enum class EImageBlitMode : uint8_t
 	/// RGBA: full alpha transparency range, e.g. shadows
 	COLORKEY,
 
-	/// Should be avoided if possible, use only for images that use def's with semi-transparency
-	/// Indexed or RGBA: Image might have full alpha transparency range, e.g. shadows
-	ALPHA
+	/// Full transparency including shadow, but treated as a single image
+	/// Indexed: Image can have alpha transparency, e.g. shadow
+	/// RGBA: full alpha transparency range, e.g. shadows
+	/// Upscaled form: single image, no option to display shadow separately
+	SIMPLE,
+
+	/// RGBA, may consist from 2 separate parts: base and shadow, overlay not preset or treated as part of body
+	WITH_SHADOW,
+
+	/// RGBA, may consist from 3 separate parts: base, shadow, and overlay
+	WITH_SHADOW_AND_OVERLAY,
+
+	/// RGBA, contains only body, with shadow and overlay disabled
+	ONLY_BODY,
+
+	/// RGBA, contains only body, with shadow disabled and overlay treated as part of body
+	ONLY_BODY_IGNORE_OVERLAY,
+
+	/// RGBA, contains only shadow
+	ONLY_SHADOW,
+
+	/// RGBA, contains only overlay
+	ONLY_OVERLAY,
 };
 
 /// Base class for images for use in client code.
@@ -75,9 +95,6 @@ public:
 	//only indexed bitmaps with 7 special colors
 	virtual void setOverlayColor(const ColorRGBA & color) = 0;
 
-	virtual void setShadowEnabled(bool on) = 0;
-	virtual void setBodyEnabled(bool on) = 0;
-	virtual void setOverlayEnabled(bool on) = 0;
 	virtual std::shared_ptr<const ISharedImage> getSharedImage() const = 0;
 
 	virtual ~IImage() = default;

+ 6 - 2
client/render/ImageLocator.cpp

@@ -124,8 +124,12 @@ std::string ImageLocator::toString() const
 	if (playerColored.isValidPlayer())
 		result += "-player" + playerColored.toString();
 
-	if (layer != EImageLayer::ALL)
-		result += "-layer" + std::to_string(static_cast<int>(layer));
+	if (layer == EImageBlitMode::ONLY_OVERLAY)
+		result += "-overlay";
+
+	if (layer == EImageBlitMode::ONLY_SHADOW)
+		result += "-shadow";
+
 
 	return result;
 }

+ 4 - 11
client/render/ImageLocator.h

@@ -9,18 +9,11 @@
  */
 #pragma once
 
+#include "IImage.h"
+
 #include "../../lib/filesystem/ResourcePath.h"
 #include "../../lib/constants/EntityIdentifiers.h"
 
-enum class EImageLayer
-{
-	ALL,
-
-	BODY,
-	SHADOW,
-	OVERLAY,
-};
-
 struct ImageLocator
 {
 	std::optional<ImagePath> image;
@@ -28,13 +21,13 @@ struct ImageLocator
 	int defFrame = -1;
 	int defGroup = -1;
 
-	PlayerColor playerColored = PlayerColor::CANNOT_DETERMINE;
+	PlayerColor playerColored = PlayerColor::CANNOT_DETERMINE; // FIXME: treat as identical to blue to avoid double-loading?
 
 	bool verticalFlip = false;
 	bool horizontalFlip = false;
 	int8_t scalingFactor = 0; // 0 = auto / use default scaling
 	int8_t preScaledFactor = 1;
-	EImageLayer layer = EImageLayer::ALL;
+	EImageBlitMode layer = EImageBlitMode::OPAQUE;
 
 	ImageLocator() = default;
 	ImageLocator(const AnimationPath & path, int frame, int group);

+ 52 - 33
client/renderSDL/ImageScaled.cpp

@@ -28,9 +28,7 @@ ImageScaled::ImageScaled(const ImageLocator & inputLocator, const std::shared_pt
 	, alphaValue(SDL_ALPHA_OPAQUE)
 	, blitMode(mode)
 {
-	setBodyEnabled(true);
-	if (mode == EImageBlitMode::ALPHA)
-		setShadowEnabled(true);
+	prepareImages();
 }
 
 std::shared_ptr<const ISharedImage> ImageScaled::getSharedImage() const
@@ -92,8 +90,7 @@ void ImageScaled::setOverlayColor(const ColorRGBA & color)
 void ImageScaled::playerColored(PlayerColor player)
 {
 	playerColor = player;
-	if (body)
-		setBodyEnabled(true); // regenerate
+	prepareImages();
 }
 
 void ImageScaled::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove)
@@ -106,41 +103,63 @@ void ImageScaled::adjustPalette(const ColorFilter &shifter, uint32_t colorsToSki
 	// TODO: implement
 }
 
-void ImageScaled::setShadowEnabled(bool on)
+void ImageScaled::prepareImages()
 {
-	assert(blitMode == EImageBlitMode::ALPHA);
-	if (on)
+	switch(blitMode)
 	{
-		locator.layer = EImageLayer::SHADOW;
-		locator.playerColored = PlayerColor::CANNOT_DETERMINE;
-		shadow = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
+		case EImageBlitMode::OPAQUE:
+		case EImageBlitMode::COLORKEY:
+		case EImageBlitMode::SIMPLE:
+			locator.layer = blitMode;
+			locator.playerColored = playerColor;
+			body = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
+			break;
+
+		case EImageBlitMode::WITH_SHADOW_AND_OVERLAY:
+		case EImageBlitMode::ONLY_BODY:
+			locator.layer = EImageBlitMode::ONLY_BODY;
+			locator.playerColored = playerColor;
+			body = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
+			break;
+
+		case EImageBlitMode::WITH_SHADOW:
+		case EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY:
+			locator.layer = EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY;
+			locator.playerColored = playerColor;
+			body = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
+			break;
+
+		case EImageBlitMode::ONLY_SHADOW:
+		case EImageBlitMode::ONLY_OVERLAY:
+			body = nullptr;
+			break;
 	}
-	else
-		shadow = nullptr;
-}
 
-void ImageScaled::setBodyEnabled(bool on)
-{
-	if (on)
+	switch(blitMode)
 	{
-		locator.layer = blitMode == EImageBlitMode::ALPHA ? EImageLayer::BODY : EImageLayer::ALL;
-		locator.playerColored = playerColor;
-		body = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
+		case EImageBlitMode::SIMPLE:
+		case EImageBlitMode::WITH_SHADOW:
+		case EImageBlitMode::ONLY_SHADOW:
+		case EImageBlitMode::WITH_SHADOW_AND_OVERLAY:
+			locator.layer = EImageBlitMode::ONLY_SHADOW;
+			locator.playerColored = PlayerColor::CANNOT_DETERMINE;
+			shadow = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
+			break;
+		default:
+			shadow = nullptr;
+			break;
 	}
-	else
-		body = nullptr;
-}
-
 
-void ImageScaled::setOverlayEnabled(bool on)
-{
-	assert(blitMode == EImageBlitMode::ALPHA);
-	if (on)
+	switch(blitMode)
 	{
-		locator.layer = EImageLayer::OVERLAY;
-		locator.playerColored = PlayerColor::CANNOT_DETERMINE;
-		overlay = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
+		case EImageBlitMode::ONLY_OVERLAY:
+		case EImageBlitMode::WITH_SHADOW_AND_OVERLAY:
+			locator.layer = EImageBlitMode::ONLY_OVERLAY;
+			locator.playerColored = PlayerColor::CANNOT_DETERMINE;
+			overlay = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
+			break;
+		default:
+			overlay = nullptr;
+			break;
 	}
-	else
-		overlay = nullptr;
 }

+ 1 - 3
client/renderSDL/ImageScaled.h

@@ -44,6 +44,7 @@ private:
 	uint8_t alphaValue;
 	EImageBlitMode blitMode;
 
+	void prepareImages();
 public:
 	ImageScaled(const ImageLocator & locator, const std::shared_ptr<const ISharedImage> & source, EImageBlitMode mode);
 
@@ -60,8 +61,5 @@ public:
 	void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override;
 	void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override;
 
-	void setShadowEnabled(bool on) override;
-	void setBodyEnabled(bool on) override;
-	void setOverlayEnabled(bool on) override;
 	std::shared_ptr<const ISharedImage> getSharedImage() const override;
 };

+ 5 - 13
client/renderSDL/RenderHandler.cpp

@@ -303,22 +303,14 @@ std::shared_ptr<const ISharedImage> RenderHandler::scaleImage(const ImageLocator
 	if (imageFiles.count(locator))
 		return imageFiles.at(locator);
 
-	auto handle = image->createImageReference(locator.layer == EImageLayer::ALL ? EImageBlitMode::OPAQUE : EImageBlitMode::ALPHA);
+	auto handle = image->createImageReference(locator.layer);
 
 	assert(locator.scalingFactor != 1); // should be filtered-out before
-
-	handle->setBodyEnabled(locator.layer == EImageLayer::ALL || locator.layer == EImageLayer::BODY);
-	if (locator.layer != EImageLayer::ALL)
-	{
-		handle->setOverlayEnabled(locator.layer == EImageLayer::OVERLAY);
-		handle->setShadowEnabled( locator.layer == EImageLayer::SHADOW);
-	}
-	if (locator.layer == EImageLayer::ALL && locator.playerColored != PlayerColor::CANNOT_DETERMINE)
+	if (locator.playerColored != PlayerColor::CANNOT_DETERMINE)
 		handle->playerColored(locator.playerColored);
 
 	handle->scaleInteger(locator.scalingFactor);
 
-	// TODO: try to optimize image size (possibly even before scaling?) - trim image borders if they are completely transparent
 	auto result = handle->getSharedImage();
 	storeCachedImage(locator, result);
 	return result;
@@ -331,9 +323,9 @@ std::shared_ptr<IImage> RenderHandler::loadImage(const ImageLocator & locator, E
 	if(adjustedLocator.image)
 	{
 		std::string imgPath = (*adjustedLocator.image).getName();
-		if(adjustedLocator.layer == EImageLayer::OVERLAY)
+		if(adjustedLocator.layer == EImageBlitMode::ONLY_OVERLAY)
 			imgPath += "-OVERLAY";
-		if(adjustedLocator.layer == EImageLayer::SHADOW)
+		if(adjustedLocator.layer == EImageBlitMode::ONLY_SHADOW)
 			imgPath += "-SHADOW";
 
 		if(CResourceHandler::get()->existsResource(ImagePath::builtin(imgPath)) ||
@@ -394,7 +386,7 @@ std::shared_ptr<IImage> RenderHandler::loadImage(const ImagePath & path, EImageB
 
 std::shared_ptr<IImage> RenderHandler::createImage(SDL_Surface * source)
 {
-	return std::make_shared<SDLImageShared>(source)->createImageReference(EImageBlitMode::ALPHA);
+	return std::make_shared<SDLImageShared>(source)->createImageReference(EImageBlitMode::SIMPLE);
 }
 
 std::shared_ptr<CAnimation> RenderHandler::loadAnimation(const AnimationPath & path, EImageBlitMode mode)

+ 36 - 50
client/renderSDL/SDLImage.cpp

@@ -180,7 +180,7 @@ void SDLImageShared::draw(SDL_Surface * where, SDL_Palette * palette, const Poin
 	if (palette && surf->format->palette)
 		SDL_SetSurfacePalette(surf, palette);
 
-	if(surf->format->palette && mode == EImageBlitMode::ALPHA)
+	if(surf->format->palette && mode != EImageBlitMode::OPAQUE && mode != EImageBlitMode::COLORKEY)
 	{
 		CSDL_Ext::blit8bppAlphaTo24bpp(surf, sourceRect, where, destShift, alpha);
 	}
@@ -425,7 +425,7 @@ void SDLImageIndexed::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove,
 void SDLImageIndexed::adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask)
 {
 	// If shadow is enabled, following colors must be skipped unconditionally
-	if (shadowEnabled)
+	if (blitMode == EImageBlitMode::WITH_SHADOW || blitMode == EImageBlitMode::WITH_SHADOW_AND_OVERLAY)
 		colorsToSkipMask |= (1 << 0) + (1 << 1) + (1 << 4);
 
 	// Note: here we skip first colors in the palette that are predefined in H3 images
@@ -445,15 +445,10 @@ SDLImageIndexed::SDLImageIndexed(const std::shared_ptr<const ISharedImage> & ima
 	:SDLImageBase::SDLImageBase(image, mode)
 	,originalPalette(originalPalette)
 {
-
 	currentPalette = SDL_AllocPalette(originalPalette->ncolors);
 	SDL_SetPaletteColors(currentPalette, originalPalette->colors, 0, originalPalette->ncolors);
 
-	if (mode == EImageBlitMode::ALPHA)
-	{
-		setOverlayColor(Colors::TRANSPARENCY);
-		setShadowTransparency(1.0);
-	}
+	preparePalette();
 }
 
 SDLImageIndexed::~SDLImageIndexed()
@@ -500,36 +495,42 @@ void SDLImageIndexed::setOverlayColor(const ColorRGBA & color)
 	}
 }
 
-void SDLImageIndexed::setShadowEnabled(bool on)
-{
-	if (on)
-		setShadowTransparency(1.0);
-
-	if (!on && blitMode == EImageBlitMode::ALPHA)
-		setShadowTransparency(0.0);
-
-	shadowEnabled = on;
-}
-
-void SDLImageIndexed::setBodyEnabled(bool on)
+void SDLImageIndexed::preparePalette()
 {
-	if (on)
-		adjustPalette(ColorFilter::genEmptyShifter(), 0);
-	else
-		adjustPalette(ColorFilter::genAlphaShifter(0), 0);
-
-	bodyEnabled = on;
-}
-
-void SDLImageIndexed::setOverlayEnabled(bool on)
-{
-	if (on)
-		setOverlayColor(Colors::WHITE_TRUE);
+	switch(blitMode)
+	{
+		case EImageBlitMode::ONLY_SHADOW:
+		case EImageBlitMode::ONLY_OVERLAY:
+			adjustPalette(ColorFilter::genAlphaShifter(0), 0);
+			break;
+	}
 
-	if (!on && blitMode == EImageBlitMode::ALPHA)
-		setOverlayColor(Colors::TRANSPARENCY);
+	switch(blitMode)
+	{
+		case EImageBlitMode::SIMPLE:
+		case EImageBlitMode::WITH_SHADOW:
+		case EImageBlitMode::ONLY_SHADOW:
+		case EImageBlitMode::WITH_SHADOW_AND_OVERLAY:
+			setShadowTransparency(1.0);
+			break;
+		case EImageBlitMode::ONLY_BODY:
+		case EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY:
+		case EImageBlitMode::ONLY_OVERLAY:
+			setShadowTransparency(0.0);
+			break;
+	}
 
-	overlayEnabled = on;
+	switch(blitMode)
+	{
+		case EImageBlitMode::ONLY_OVERLAY:
+		case EImageBlitMode::WITH_SHADOW_AND_OVERLAY:
+			setOverlayColor(Colors::WHITE_TRUE);
+			break;
+		case EImageBlitMode::ONLY_SHADOW:
+		case EImageBlitMode::ONLY_BODY:
+			setOverlayColor(Colors::TRANSPARENCY);
+			break;
+	}
 }
 
 SDLImageShared::~SDLImageShared()
@@ -609,21 +610,6 @@ void SDLImageBase::setBlitMode(EImageBlitMode mode)
 	blitMode = mode;
 }
 
-void SDLImageRGB::setShadowEnabled(bool on)
-{
-	// Not supported. Theoretically we can try to extract all pixels of specific colors, but better to use 8-bit images or composite images
-}
-
-void SDLImageRGB::setBodyEnabled(bool on)
-{
-	// Not supported. Theoretically we can try to extract all pixels of specific colors, but better to use 8-bit images or composite images
-}
-
-void SDLImageRGB::setOverlayEnabled(bool on)
-{
-	// Not supported. Theoretically we can try to extract all pixels of specific colors, but better to use 8-bit images or composite images
-}
-
 void SDLImageRGB::setOverlayColor(const ColorRGBA & color)
 {}
 

+ 1 - 12
client/renderSDL/SDLImage.h

@@ -89,11 +89,8 @@ class SDLImageIndexed final : public SDLImageBase
 	SDL_Palette * currentPalette = nullptr;
 	SDL_Palette * originalPalette = nullptr;
 
-	bool bodyEnabled = true;
-	bool shadowEnabled = false;
-	bool overlayEnabled = false;
-
 	void setShadowTransparency(float factor);
+	void preparePalette();
 public:
 	SDLImageIndexed(const std::shared_ptr<const ISharedImage> & image, SDL_Palette * palette, EImageBlitMode mode);
 	~SDLImageIndexed();
@@ -106,10 +103,6 @@ public:
 	void scaleInteger(int factor) override;
 	void scaleTo(const Point & size) override;
 	void exportBitmap(const boost::filesystem::path & path) const override;
-
-	void setShadowEnabled(bool on) override;
-	void setBodyEnabled(bool on) override;
-	void setOverlayEnabled(bool on) override;
 };
 
 class SDLImageRGB final : public SDLImageBase
@@ -125,8 +118,4 @@ public:
 	void scaleInteger(int factor) override;
 	void scaleTo(const Point & size) override;
 	void exportBitmap(const boost::filesystem::path & path) const override;
-
-	void setShadowEnabled(bool on) override;
-	void setBodyEnabled(bool on) override;
-	void setOverlayEnabled(bool on) override;
 };

+ 3 - 5
client/widgets/Images.cpp

@@ -194,12 +194,12 @@ CAnimImage::CAnimImage(const AnimationPath & name, size_t Frame, size_t Group, i
 {
 	pos.x += x;
 	pos.y += y;
-	anim = GH.renderHandler().loadAnimation(name, EImageBlitMode::COLORKEY);
+	anim = GH.renderHandler().loadAnimation(name, (Flags & CCreatureAnim::CREATURE_MODE) ? EImageBlitMode::WITH_SHADOW_AND_OVERLAY : EImageBlitMode::COLORKEY);
 	init();
 }
 
 CAnimImage::CAnimImage(const AnimationPath & name, size_t Frame, Rect targetPos, size_t Group, ui8 Flags):
-	anim(GH.renderHandler().loadAnimation(name, EImageBlitMode::COLORKEY)),
+	anim(GH.renderHandler().loadAnimation(name, (Flags & CCreatureAnim::CREATURE_MODE) ? EImageBlitMode::WITH_SHADOW_AND_OVERLAY : EImageBlitMode::COLORKEY)),
 	frame(Frame),
 	group(Group),
 	flags(Flags),
@@ -317,7 +317,7 @@ bool CAnimImage::isPlayerColored() const
 }
 
 CShowableAnim::CShowableAnim(int x, int y, const AnimationPath & name, ui8 Flags, ui32 frameTime, size_t Group, uint8_t alpha):
-	anim(GH.renderHandler().loadAnimation(name, (Flags & CREATURE_MODE) ? EImageBlitMode::ALPHA : EImageBlitMode::COLORKEY)),
+	anim(GH.renderHandler().loadAnimation(name, (Flags & CREATURE_MODE) ? EImageBlitMode::WITH_SHADOW_AND_OVERLAY : EImageBlitMode::COLORKEY)),
 	group(Group),
 	frame(0),
 	first(0),
@@ -430,8 +430,6 @@ void CShowableAnim::blitImage(size_t frame, size_t group, Canvas & to)
 	auto img = anim->getImage(frame, group);
 	if(img)
 	{
-		if (flags & CREATURE_MODE)
-			img->setShadowEnabled(true);
 		img->setAlpha(alpha);
 		to.draw(img, pos.topLeft(), src);
 	}

+ 1 - 1
client/windows/CCastleInterface.cpp

@@ -98,7 +98,7 @@ CBuildingRect::CBuildingRect(CCastleBuildings * Par, const CGTownInstance * Town
 		border = GH.renderHandler().loadImage(str->borderName, EImageBlitMode::COLORKEY);
 
 	if(!str->areaName.empty())
-		area = GH.renderHandler().loadImage(str->areaName, EImageBlitMode::ALPHA);
+		area = GH.renderHandler().loadImage(str->areaName, EImageBlitMode::SIMPLE);
 }
 
 const CBuilding * CBuildingRect::getBuilding()

+ 1 - 1
client/windows/GUIClasses.cpp

@@ -950,7 +950,7 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, BuildingID bu
 	}
 	else if(auto uni = dynamic_cast<const CGUniversity *>(_market); uni->appearance)
 	{
-		titlePic = std::make_shared<CAnimImage>(uni->appearance->animationFile, 0);
+		titlePic = std::make_shared<CAnimImage>(uni->appearance->animationFile, 0, 0, 0, 0, CShowableAnim::CREATURE_MODE);
 		titleStr = uni->title;
 		speechStr = uni->speech;
 	}