mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	More robust management of body/shadow/overlay split
This commit is contained in:
		| @@ -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()); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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(); | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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)); | ||||
|  | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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)) | ||||
| { | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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(); | ||||
| 	} | ||||
| 	else | ||||
| 		shadow = nullptr; | ||||
| } | ||||
| 		case EImageBlitMode::OPAQUE: | ||||
| 		case EImageBlitMode::COLORKEY: | ||||
| 		case EImageBlitMode::SIMPLE: | ||||
| 			locator.layer = blitMode; | ||||
| 			locator.playerColored = playerColor; | ||||
| 			body = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage(); | ||||
| 			break; | ||||
|  | ||||
| void ImageScaled::setBodyEnabled(bool on) | ||||
| { | ||||
| 	if (on) | ||||
| 		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; | ||||
| 	} | ||||
|  | ||||
| 	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; | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
| }; | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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) | ||||
| void SDLImageIndexed::preparePalette() | ||||
| { | ||||
| 	if (on) | ||||
| 		setShadowTransparency(1.0); | ||||
| 	switch(blitMode) | ||||
| 	{ | ||||
| 		case EImageBlitMode::ONLY_SHADOW: | ||||
| 		case EImageBlitMode::ONLY_OVERLAY: | ||||
| 			adjustPalette(ColorFilter::genAlphaShifter(0), 0); | ||||
| 			break; | ||||
| 	} | ||||
|  | ||||
| 	if (!on && blitMode == EImageBlitMode::ALPHA) | ||||
| 		setShadowTransparency(0.0); | ||||
| 	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; | ||||
| 	} | ||||
|  | ||||
| 	shadowEnabled = on; | ||||
| } | ||||
|  | ||||
| void SDLImageIndexed::setBodyEnabled(bool on) | ||||
| { | ||||
| 	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); | ||||
|  | ||||
| 	if (!on && blitMode == EImageBlitMode::ALPHA) | ||||
| 		setOverlayColor(Colors::TRANSPARENCY); | ||||
|  | ||||
| 	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) | ||||
| {} | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
| }; | ||||
|   | ||||
| @@ -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); | ||||
| 	} | ||||
|   | ||||
| @@ -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() | ||||
|   | ||||
| @@ -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; | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user