mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Support for shadow, overlay and player-colored premade images for 1x
This commit is contained in:
		| @@ -26,6 +26,7 @@ | ||||
| #include "../../lib/CThreadHelper.h" | ||||
| #include "../../lib/filesystem/Filesystem.h" | ||||
| #include "../../lib/VCMIDirs.h" | ||||
| #include "../../lib/constants/StringConstants.h" | ||||
|  | ||||
| #include <vcmi/ArtifactService.h> | ||||
| #include <vcmi/CreatureService.h> | ||||
| @@ -218,8 +219,6 @@ void RenderHandler::storeCachedImage(const ImageLocator & locator, std::shared_p | ||||
|  | ||||
| std::shared_ptr<SDLImageShared> RenderHandler::loadScaledImage(const ImageLocator & locator) | ||||
| { | ||||
| 	assert(locator.scalingFactor > 1); | ||||
|  | ||||
| 	static constexpr std::array scaledDataPath = { | ||||
| 		"", // 0x | ||||
| 		"DATA/", | ||||
| @@ -260,6 +259,10 @@ std::shared_ptr<SDLImageShared> RenderHandler::loadScaledImage(const ImageLocato | ||||
| 		imagePathString += "-OVERLAY"; | ||||
| 	if(locator.layer == EImageBlitMode::ONLY_SHADOW) | ||||
| 		imagePathString += "-SHADOW"; | ||||
| 	if(locator.playerColored.isValidPlayer()) | ||||
| 		imagePathString += "-" + boost::to_upper_copy(GameConstants::PLAYER_COLOR_NAMES[locator.playerColored.getNum()]); | ||||
| 	if(locator.playerColored == PlayerColor::NEUTRAL) | ||||
| 		imagePathString += "-NEUTRAL"; | ||||
|  | ||||
| 	auto imagePath = ImagePath::builtin(imagePathString); | ||||
| 	auto imagePathSprites = ImagePath::builtin(imagePathString).addPrefix(scaledSpritesPath.at(locator.scalingFactor)); | ||||
|   | ||||
| @@ -202,30 +202,30 @@ void ScalableImageParameters::adjustPalette(const SDL_Palette * originalPalette, | ||||
| ScalableImageShared::ScalableImageShared(const SharedImageLocator & locator, const std::shared_ptr<const ISharedImage> & baseImage) | ||||
| 	:locator(locator) | ||||
| { | ||||
| 	base[0] = baseImage; | ||||
| 	assert(base[0] != nullptr); | ||||
| 	scaled[1].body[0] = baseImage; | ||||
| 	assert(scaled[1].body[0] != nullptr); | ||||
|  | ||||
| 	loadScaledImages(GH.screenHandler().getScalingFactor(), PlayerColor::CANNOT_DETERMINE); | ||||
| } | ||||
|  | ||||
| Point ScalableImageShared::dimensions() const | ||||
| { | ||||
| 	return base[0]->dimensions(); | ||||
| 	return scaled[1].body[0]->dimensions(); | ||||
| } | ||||
|  | ||||
| void ScalableImageShared::exportBitmap(const boost::filesystem::path & path, const ScalableImageParameters & parameters) const | ||||
| { | ||||
| 	base[0]->exportBitmap(path, parameters.palette); | ||||
| 	scaled[1].body[0]->exportBitmap(path, parameters.palette); | ||||
| } | ||||
|  | ||||
| bool ScalableImageShared::isTransparent(const Point & coords) const | ||||
| { | ||||
| 	return base[0]->isTransparent(coords); | ||||
| 	return scaled[1].body[0]->isTransparent(coords); | ||||
| } | ||||
|  | ||||
| Rect ScalableImageShared::contentRect() const | ||||
| { | ||||
| 	return base[0]->contentRect(); | ||||
| 	return scaled[1].body[0]->contentRect(); | ||||
| } | ||||
|  | ||||
| void ScalableImageShared::draw(SDL_Surface * where, const Point & dest, const Rect * src, const ScalableImageParameters & parameters, int scalingFactor) | ||||
| @@ -256,44 +256,37 @@ void ScalableImageShared::draw(SDL_Surface * where, const Point & dest, const Re | ||||
| 		getFlippedImage(images)->draw(where, parameters.palette, dest, src, colorMultiplier, alphaValue, locator.layer); | ||||
| 	}; | ||||
|  | ||||
| 	if (scalingFactor == 1) | ||||
| 	bool shadowLoading = scaled.at(scalingFactor).shadow.at(0) && scaled.at(scalingFactor).shadow.at(0)->isLoading(); | ||||
| 	bool bodyLoading = scaled.at(scalingFactor).body.at(0) && scaled.at(scalingFactor).body.at(0)->isLoading(); | ||||
| 	bool overlayLoading = scaled.at(scalingFactor).overlay.at(0) && scaled.at(scalingFactor).overlay.at(0)->isLoading(); | ||||
| 	bool playerLoading = parameters.player != PlayerColor::CANNOT_DETERMINE && scaled.at(scalingFactor).playerColored.at(1+parameters.player.getNum()) && scaled.at(scalingFactor).playerColored.at(1+parameters.player.getNum())->isLoading(); | ||||
|  | ||||
| 	if (shadowLoading || bodyLoading || overlayLoading || playerLoading) | ||||
| 	{ | ||||
| 		flipAndDraw(base, parameters.colorMultiplier, parameters.alphaValue); | ||||
| 		getFlippedImage(scaled[1].body)->scaledDraw(where, parameters.palette, dimensions() * scalingFactor, dest, src, parameters.colorMultiplier, parameters.alphaValue, locator.layer); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if (scaled.at(scalingFactor).shadow.at(0)) | ||||
| 		flipAndDraw(scaled.at(scalingFactor).shadow, Colors::WHITE_TRUE, parameters.alphaValue); | ||||
|  | ||||
| 	if (parameters.player != PlayerColor::CANNOT_DETERMINE && scaled.at(scalingFactor).playerColored.at(1+parameters.player.getNum())) | ||||
| 	{ | ||||
| 		scaled.at(scalingFactor).playerColored.at(1+parameters.player.getNum())->draw(where, parameters.palette, dest, src, Colors::WHITE_TRUE, parameters.alphaValue, locator.layer); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		bool shadowLoading = scaled.at(scalingFactor).shadow.at(0) && scaled.at(scalingFactor).shadow.at(0)->isLoading(); | ||||
| 		bool bodyLoading = scaled.at(scalingFactor).body.at(0) && scaled.at(scalingFactor).body.at(0)->isLoading(); | ||||
| 		bool overlayLoading = scaled.at(scalingFactor).overlay.at(0) && scaled.at(scalingFactor).overlay.at(0)->isLoading(); | ||||
| 		bool playerLoading = parameters.player.isValidPlayer() && scaled.at(scalingFactor).playerColored.at(parameters.player.getNum()) && scaled.at(scalingFactor).playerColored.at(parameters.player.getNum())->isLoading(); | ||||
|  | ||||
| 		if (shadowLoading || bodyLoading || overlayLoading || playerLoading) | ||||
| 		{ | ||||
| 			getFlippedImage(base)->scaledDraw(where, parameters.palette, dimensions() * scalingFactor, dest, src, parameters.colorMultiplier, parameters.alphaValue, locator.layer); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		if (scaled.at(scalingFactor).shadow.at(0)) | ||||
| 			flipAndDraw(scaled.at(scalingFactor).shadow, Colors::WHITE_TRUE, parameters.alphaValue); | ||||
|  | ||||
| 		if (parameters.player.isValidPlayer()) | ||||
| 		{ | ||||
| 			scaled.at(scalingFactor).playerColored.at(parameters.player.getNum())->draw(where, parameters.palette, dest, src, Colors::WHITE_TRUE, parameters.alphaValue, locator.layer); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			if (scaled.at(scalingFactor).body.at(0)) | ||||
| 				flipAndDraw(scaled.at(scalingFactor).body, parameters.colorMultiplier, parameters.alphaValue); | ||||
| 		} | ||||
|  | ||||
| 		if (scaled.at(scalingFactor).overlay.at(0)) | ||||
| 			flipAndDraw(scaled.at(scalingFactor).overlay, parameters.ovelayColorMultiplier, static_cast<int>(parameters.alphaValue) * parameters.ovelayColorMultiplier.a / 255); | ||||
| 		if (scaled.at(scalingFactor).body.at(0)) | ||||
| 			flipAndDraw(scaled.at(scalingFactor).body, parameters.colorMultiplier, parameters.alphaValue); | ||||
| 	} | ||||
|  | ||||
| 	if (scaled.at(scalingFactor).overlay.at(0)) | ||||
| 		flipAndDraw(scaled.at(scalingFactor).overlay, parameters.ovelayColorMultiplier, static_cast<int>(parameters.alphaValue) * parameters.ovelayColorMultiplier.a / 255); | ||||
| } | ||||
|  | ||||
| const SDL_Palette * ScalableImageShared::getPalette() const | ||||
| { | ||||
| 	return base[0]->getPalette(); | ||||
| 	return scaled[1].body[0]->getPalette(); | ||||
| } | ||||
|  | ||||
| std::shared_ptr<ScalableImageInstance> ScalableImageShared::createImageReference() | ||||
| @@ -400,7 +393,7 @@ void ScalableImageInstance::verticalFlip() | ||||
| 	parameters.flipVertical = !parameters.flipVertical; | ||||
| } | ||||
|  | ||||
| std::shared_ptr<const ISharedImage> ScalableImageShared::loadOrGenerateImage(EImageBlitMode mode, int8_t scalingFactor, PlayerColor color) const | ||||
| std::shared_ptr<const ISharedImage> ScalableImageShared::loadOrGenerateImage(EImageBlitMode mode, int8_t scalingFactor, PlayerColor color, ImageType upscalingSource) const | ||||
| { | ||||
| 	ImageLocator loadingLocator; | ||||
|  | ||||
| @@ -417,68 +410,90 @@ std::shared_ptr<const ISharedImage> ScalableImageShared::loadOrGenerateImage(EIm | ||||
| 	if (loadedImage) | ||||
| 		return loadedImage; | ||||
|  | ||||
| 	if (scalingFactor == 1) | ||||
| 	{ | ||||
| 		// optional images for 1x resolution - only try load them, don't attempt to generate | ||||
| 		// this block should never be called for 'body' layer - that image is loaded unconditionally before construction | ||||
| 		assert(mode == EImageBlitMode::ONLY_SHADOW || mode == EImageBlitMode::ONLY_OVERLAY || color != PlayerColor::CANNOT_DETERMINE); | ||||
| 		return nullptr; | ||||
| 	} | ||||
|  | ||||
| 	// alternatively, find largest pre-scaled image, load it and rescale to desired scaling | ||||
| 	Point targetSize = base[0]->dimensions() * scalingFactor; | ||||
| 	for (int8_t scaling = 4; scaling > 1; --scaling) | ||||
| 	for (int8_t scaling = 4; scaling > 0; --scaling) | ||||
| 	{ | ||||
| 		loadingLocator.scalingFactor = scaling; | ||||
| 		auto loadedImage = GH.renderHandler().loadScaledImage(loadingLocator); | ||||
| 		if (loadedImage) | ||||
| 			return loadedImage->scaleTo(targetSize, nullptr); | ||||
| 		{ | ||||
| 			if (scaling == 1) | ||||
| 			{ | ||||
| 				if (mode == EImageBlitMode::ONLY_SHADOW || mode == EImageBlitMode::ONLY_OVERLAY || color != PlayerColor::CANNOT_DETERMINE) | ||||
| 				{ | ||||
| 					ScalableImageParameters parameters(getPalette(), mode); | ||||
| 					return loadedImage->scaleInteger(scalingFactor, parameters.palette, mode); | ||||
| 				} | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				Point targetSize = scaled[1].body[0]->dimensions() * scalingFactor; | ||||
| 				return loadedImage->scaleTo(targetSize, nullptr); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// if all else fails - use base (presumably, indexed) image and convert it to desired form | ||||
| 	ScalableImageParameters parameters(getPalette(), mode); | ||||
| 	// if all else fails - use base (presumably, indexed) image and convert it to desired form | ||||
| 	if (color != PlayerColor::CANNOT_DETERMINE) | ||||
| 		parameters.playerColored(color); | ||||
| 	return base[0]->scaleInteger(scalingFactor, parameters.palette, mode); | ||||
|  | ||||
| 	if (upscalingSource) | ||||
| 		return upscalingSource->scaleInteger(scalingFactor, parameters.palette, mode); | ||||
| 	else | ||||
| 		return scaled[1].body[0]->scaleInteger(scalingFactor, parameters.palette, mode); | ||||
| } | ||||
|  | ||||
| void ScalableImageShared::loadScaledImages(int8_t scalingFactor, PlayerColor color) | ||||
| { | ||||
| 	if (scalingFactor == 1) | ||||
| 		return; // no op. TODO: consider loading 1x images for mods, as alternative to palette-based animations | ||||
|  | ||||
| 	if (scaled[scalingFactor].body[0] == nullptr) | ||||
| 	if (scaled[scalingFactor].body[0] == nullptr && scalingFactor != 1) | ||||
| 	{ | ||||
| 		switch(locator.layer) | ||||
| 		{ | ||||
| 			case EImageBlitMode::OPAQUE: | ||||
| 			case EImageBlitMode::COLORKEY: | ||||
| 			case EImageBlitMode::SIMPLE: | ||||
| 				scaled[scalingFactor].body[0] = loadOrGenerateImage(locator.layer, scalingFactor, PlayerColor::CANNOT_DETERMINE); | ||||
| 				scaled[scalingFactor].body[0] = loadOrGenerateImage(locator.layer, scalingFactor, PlayerColor::CANNOT_DETERMINE, scaled[1].body[0]); | ||||
| 				break; | ||||
|  | ||||
| 			case EImageBlitMode::WITH_SHADOW_AND_OVERLAY: | ||||
| 			case EImageBlitMode::ONLY_BODY: | ||||
| 				scaled[scalingFactor].body[0] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY, scalingFactor, PlayerColor::CANNOT_DETERMINE); | ||||
| 				scaled[scalingFactor].body[0] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY, scalingFactor, PlayerColor::CANNOT_DETERMINE, scaled[1].body[0]); | ||||
| 				break; | ||||
|  | ||||
| 			case EImageBlitMode::WITH_SHADOW: | ||||
| 			case EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY: | ||||
| 				scaled[scalingFactor].body[0] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY, scalingFactor, PlayerColor::CANNOT_DETERMINE); | ||||
| 				scaled[scalingFactor].body[0] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY, scalingFactor, PlayerColor::CANNOT_DETERMINE, scaled[1].body[0]); | ||||
| 				break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (color.isValidPlayer() && scaled[scalingFactor].playerColored[color.getNum()] == nullptr) | ||||
| 	if (color != PlayerColor::CANNOT_DETERMINE && scaled[scalingFactor].playerColored[1+color.getNum()] == nullptr) | ||||
| 	{ | ||||
| 		switch(locator.layer) | ||||
| 		{ | ||||
| 			case EImageBlitMode::OPAQUE: | ||||
| 			case EImageBlitMode::COLORKEY: | ||||
| 			case EImageBlitMode::SIMPLE: | ||||
| 				scaled[scalingFactor].playerColored[color.getNum()] = loadOrGenerateImage(locator.layer, scalingFactor, color); | ||||
| 				scaled[scalingFactor].playerColored[1+color.getNum()] = loadOrGenerateImage(locator.layer, scalingFactor, color, scaled[1].playerColored[1+color.getNum()]); | ||||
| 				break; | ||||
|  | ||||
| 			case EImageBlitMode::WITH_SHADOW_AND_OVERLAY: | ||||
| 			case EImageBlitMode::ONLY_BODY: | ||||
| 				scaled[scalingFactor].playerColored[color.getNum()] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY, scalingFactor, color); | ||||
| 				scaled[scalingFactor].playerColored[1+color.getNum()] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY, scalingFactor, color, scaled[1].playerColored[1+color.getNum()]); | ||||
| 				break; | ||||
|  | ||||
| 			case EImageBlitMode::WITH_SHADOW: | ||||
| 			case EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY: | ||||
| 				scaled[scalingFactor].playerColored[color.getNum()] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY, scalingFactor, color); | ||||
| 				scaled[scalingFactor].playerColored[1+color.getNum()] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY, scalingFactor, color, scaled[1].playerColored[1+color.getNum()]); | ||||
| 				break; | ||||
| 		} | ||||
| 	} | ||||
| @@ -490,7 +505,7 @@ void ScalableImageShared::loadScaledImages(int8_t scalingFactor, PlayerColor col | ||||
| 			case EImageBlitMode::WITH_SHADOW: | ||||
| 			case EImageBlitMode::ONLY_SHADOW: | ||||
| 			case EImageBlitMode::WITH_SHADOW_AND_OVERLAY: | ||||
| 				scaled[scalingFactor].shadow[0] = loadOrGenerateImage(EImageBlitMode::ONLY_SHADOW, scalingFactor, PlayerColor::CANNOT_DETERMINE); | ||||
| 				scaled[scalingFactor].shadow[0] = loadOrGenerateImage(EImageBlitMode::ONLY_SHADOW, scalingFactor, PlayerColor::CANNOT_DETERMINE, scaled[1].shadow[0]); | ||||
| 				break; | ||||
| 			default: | ||||
| 				break; | ||||
| @@ -503,7 +518,7 @@ void ScalableImageShared::loadScaledImages(int8_t scalingFactor, PlayerColor col | ||||
| 		{ | ||||
| 			case EImageBlitMode::ONLY_OVERLAY: | ||||
| 			case EImageBlitMode::WITH_SHADOW_AND_OVERLAY: | ||||
| 				scaled[scalingFactor].overlay[0] = loadOrGenerateImage(EImageBlitMode::ONLY_OVERLAY, scalingFactor, PlayerColor::CANNOT_DETERMINE); | ||||
| 				scaled[scalingFactor].overlay[0] = loadOrGenerateImage(EImageBlitMode::ONLY_OVERLAY, scalingFactor, PlayerColor::CANNOT_DETERMINE, scaled[1].overlay[0]); | ||||
| 				break; | ||||
| 			default: | ||||
| 				break; | ||||
|   | ||||
| @@ -46,11 +46,12 @@ struct ScalableImageParameters : boost::noncopyable | ||||
|  | ||||
| class ScalableImageShared final : public std::enable_shared_from_this<ScalableImageShared>, boost::noncopyable | ||||
| { | ||||
| 	static constexpr int scalingSize = 5; // 0-4 range. TODO: switch to either 2-4 or 1-4 | ||||
| 	static constexpr int scalingSize = 5; // 0-4 range. TODO: switch to 1-4 since there is no '0' scaling | ||||
| 	static constexpr int maxFlips = 4; | ||||
|  | ||||
| 	using FlippedImages = std::array<std::shared_ptr<const ISharedImage>, maxFlips>; | ||||
| 	using PlayerColoredImages = std::array<std::shared_ptr<const ISharedImage>, PlayerColor::PLAYER_LIMIT_I>; | ||||
| 	using ImageType = std::shared_ptr<const ISharedImage>; | ||||
| 	using FlippedImages = std::array<ImageType, maxFlips>; | ||||
| 	using PlayerColoredImages = std::array<ImageType, PlayerColor::PLAYER_LIMIT_I + 1>; // all valid colors+neutral | ||||
|  | ||||
| 	struct ScaledImage | ||||
| 	{ | ||||
| @@ -67,16 +68,13 @@ class ScalableImageShared final : public std::enable_shared_from_this<ScalableIm | ||||
| 		PlayerColoredImages playerColored; | ||||
| 	}; | ||||
|  | ||||
| 	/// 1x image, usually indexed. Array of 4 images for every potential flip | ||||
| 	FlippedImages base; | ||||
|  | ||||
| 	/// 1x-4x images. 1x are currently unused, but can be used for providing non-palette version of these images | ||||
| 	/// 1x-4x images. body for 1x scaling is guaranteed to be loaded | ||||
| 	std::array<ScaledImage, scalingSize> scaled; | ||||
|  | ||||
| 	/// Locator of this image, for loading additional (e.g. upscaled) images | ||||
| 	const SharedImageLocator locator; | ||||
|  | ||||
| 	std::shared_ptr<const ISharedImage> loadOrGenerateImage(EImageBlitMode mode, int8_t scalingFactor, PlayerColor color) const; | ||||
| 	std::shared_ptr<const ISharedImage> loadOrGenerateImage(EImageBlitMode mode, int8_t scalingFactor, PlayerColor color, ImageType upscalingSource) const; | ||||
|  | ||||
| 	void loadScaledImages(int8_t scalingFactor, PlayerColor color); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user