mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Reworked image container classes for easier support of new features
This commit is contained in:
		| @@ -99,7 +99,7 @@ set(vcmiclientcommon_SRCS | |||||||
| 	renderSDL/CursorHardware.cpp | 	renderSDL/CursorHardware.cpp | ||||||
| 	renderSDL/CursorSoftware.cpp | 	renderSDL/CursorSoftware.cpp | ||||||
| 	renderSDL/FontChain.cpp | 	renderSDL/FontChain.cpp | ||||||
| 	renderSDL/ImageScaled.cpp | 	renderSDL/ScalableImage.cpp | ||||||
| 	renderSDL/RenderHandler.cpp | 	renderSDL/RenderHandler.cpp | ||||||
| 	renderSDL/SDLImage.cpp | 	renderSDL/SDLImage.cpp | ||||||
| 	renderSDL/SDLImageLoader.cpp | 	renderSDL/SDLImageLoader.cpp | ||||||
| @@ -307,7 +307,7 @@ set(vcmiclientcommon_HEADERS | |||||||
| 	renderSDL/CursorHardware.h | 	renderSDL/CursorHardware.h | ||||||
| 	renderSDL/CursorSoftware.h | 	renderSDL/CursorSoftware.h | ||||||
| 	renderSDL/FontChain.h | 	renderSDL/FontChain.h | ||||||
| 	renderSDL/ImageScaled.h | 	renderSDL/ScalableImage.h | ||||||
| 	renderSDL/RenderHandler.h | 	renderSDL/RenderHandler.h | ||||||
| 	renderSDL/SDLImage.h | 	renderSDL/SDLImage.h | ||||||
| 	renderSDL/SDLImageLoader.h | 	renderSDL/SDLImageLoader.h | ||||||
|   | |||||||
| @@ -58,10 +58,9 @@ void AssetGenerator::createAdventureOptionsCleanBackground() | |||||||
| 		return; | 		return; | ||||||
| 	ResourcePath savePath(filename, EResType::IMAGE); | 	ResourcePath savePath(filename, EResType::IMAGE); | ||||||
|  |  | ||||||
| 	auto locator = ImageLocator(ImagePath::builtin("ADVOPTBK")); | 	auto locator = ImageLocator(ImagePath::builtin("ADVOPTBK"), EImageBlitMode::OPAQUE); | ||||||
| 	locator.scalingFactor = 1; |  | ||||||
|  |  | ||||||
| 	std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator, EImageBlitMode::OPAQUE); | 	std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator); | ||||||
|  |  | ||||||
| 	Canvas canvas = Canvas(Point(575, 585), CanvasScalingPolicy::IGNORE); | 	Canvas canvas = Canvas(Point(575, 585), CanvasScalingPolicy::IGNORE); | ||||||
| 	canvas.draw(img, Point(0, 0), Rect(0, 0, 575, 585)); | 	canvas.draw(img, Point(0, 0), Rect(0, 0, 575, 585)); | ||||||
| @@ -88,10 +87,9 @@ void AssetGenerator::createBigSpellBook() | |||||||
| 		return; | 		return; | ||||||
| 	ResourcePath savePath(filename, EResType::IMAGE); | 	ResourcePath savePath(filename, EResType::IMAGE); | ||||||
|  |  | ||||||
| 	auto locator = ImageLocator(ImagePath::builtin("SpelBack")); | 	auto locator = ImageLocator(ImagePath::builtin("SpelBack"), EImageBlitMode::OPAQUE); | ||||||
| 	locator.scalingFactor = 1; |  | ||||||
|  |  | ||||||
| 	std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator, EImageBlitMode::OPAQUE); | 	std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator); | ||||||
| 	Canvas canvas = Canvas(Point(800, 600), CanvasScalingPolicy::IGNORE); | 	Canvas canvas = Canvas(Point(800, 600), CanvasScalingPolicy::IGNORE); | ||||||
| 	// edges | 	// edges | ||||||
| 	canvas.draw(img, Point(0, 0), Rect(15, 38, 90, 45)); | 	canvas.draw(img, Point(0, 0), Rect(15, 38, 90, 45)); | ||||||
| @@ -152,10 +150,9 @@ void AssetGenerator::createPlayerColoredBackground(const PlayerColor & player) | |||||||
|  |  | ||||||
| 	ResourcePath savePath(filename, EResType::IMAGE); | 	ResourcePath savePath(filename, EResType::IMAGE); | ||||||
|  |  | ||||||
| 	auto locator = ImageLocator(ImagePath::builtin("DiBoxBck")); | 	auto locator = ImageLocator(ImagePath::builtin("DiBoxBck"), EImageBlitMode::OPAQUE); | ||||||
| 	locator.scalingFactor = 1; |  | ||||||
|  |  | ||||||
| 	std::shared_ptr<IImage> texture = GH.renderHandler().loadImage(locator, EImageBlitMode::OPAQUE); | 	std::shared_ptr<IImage> texture = GH.renderHandler().loadImage(locator); | ||||||
|  |  | ||||||
| 	// transform to make color of brown DIBOX.PCX texture match color of specified player | 	// transform to make color of brown DIBOX.PCX texture match color of specified player | ||||||
| 	auto filterSettings = VLC->settingsHandler->getFullConfig()["interface"]["playerColoredBackground"]; | 	auto filterSettings = VLC->settingsHandler->getFullConfig()["interface"]["playerColoredBackground"]; | ||||||
| @@ -199,10 +196,10 @@ void AssetGenerator::createCombatUnitNumberWindow() | |||||||
| 	   !CResourceHandler::get("local")->createResource(savePathNegative.getOriginalName() + ".png")) | 	   !CResourceHandler::get("local")->createResource(savePathNegative.getOriginalName() + ".png")) | ||||||
| 		return; | 		return; | ||||||
|  |  | ||||||
| 	auto locator = ImageLocator(ImagePath::builtin("CMNUMWIN")); | 	auto locator = ImageLocator(ImagePath::builtin("CMNUMWIN"), EImageBlitMode::OPAQUE); | ||||||
| 	locator.scalingFactor = 1; | 	locator.layer = EImageBlitMode::OPAQUE; | ||||||
|  |  | ||||||
| 	std::shared_ptr<IImage> texture = GH.renderHandler().loadImage(locator, EImageBlitMode::OPAQUE); | 	std::shared_ptr<IImage> texture = GH.renderHandler().loadImage(locator); | ||||||
|  |  | ||||||
| 	static const auto shifterNormal   = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 0.6f, 0.2f, 1.0f ); | 	static const auto shifterNormal   = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 0.6f, 0.2f, 1.0f ); | ||||||
| 	static const auto shifterPositive = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 0.2f, 1.0f, 0.2f ); | 	static const auto shifterPositive = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 0.2f, 1.0f, 0.2f ); | ||||||
| @@ -233,10 +230,9 @@ void AssetGenerator::createCampaignBackground() | |||||||
| 		return; | 		return; | ||||||
| 	ResourcePath savePath(filename, EResType::IMAGE); | 	ResourcePath savePath(filename, EResType::IMAGE); | ||||||
|  |  | ||||||
| 	auto locator = ImageLocator(ImagePath::builtin("CAMPBACK")); | 	auto locator = ImageLocator(ImagePath::builtin("CAMPBACK"), EImageBlitMode::OPAQUE); | ||||||
| 	locator.scalingFactor = 1; |  | ||||||
|  |  | ||||||
| 	std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator, EImageBlitMode::OPAQUE); | 	std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator); | ||||||
| 	Canvas canvas = Canvas(Point(800, 600), CanvasScalingPolicy::IGNORE); | 	Canvas canvas = Canvas(Point(800, 600), CanvasScalingPolicy::IGNORE); | ||||||
| 	 | 	 | ||||||
| 	canvas.draw(img, Point(0, 0), Rect(0, 0, 800, 600)); | 	canvas.draw(img, Point(0, 0), Rect(0, 0, 800, 600)); | ||||||
| @@ -263,9 +259,8 @@ void AssetGenerator::createCampaignBackground() | |||||||
| 	canvas.draw(img, Point(404, 414), Rect(313, 74, 197, 114)); | 	canvas.draw(img, Point(404, 414), Rect(313, 74, 197, 114)); | ||||||
|  |  | ||||||
| 	// skull | 	// skull | ||||||
| 	auto locatorSkull = ImageLocator(ImagePath::builtin("CAMPNOSC")); | 	auto locatorSkull = ImageLocator(ImagePath::builtin("CAMPNOSC"), EImageBlitMode::OPAQUE); | ||||||
| 	locatorSkull.scalingFactor = 1; | 	std::shared_ptr<IImage> imgSkull = GH.renderHandler().loadImage(locatorSkull); | ||||||
| 	std::shared_ptr<IImage> imgSkull = GH.renderHandler().loadImage(locatorSkull, EImageBlitMode::OPAQUE); |  | ||||||
| 	canvas.draw(imgSkull, Point(562, 509), Rect(178, 108, 43, 19)); | 	canvas.draw(imgSkull, Point(562, 509), Rect(178, 108, 43, 19)); | ||||||
|  |  | ||||||
| 	std::shared_ptr<IImage> image = GH.renderHandler().createImage(canvas.getInternalSurface()); | 	std::shared_ptr<IImage> image = GH.renderHandler().createImage(canvas.getInternalSurface()); | ||||||
| @@ -290,10 +285,9 @@ void AssetGenerator::createChroniclesCampaignImages() | |||||||
| 			continue; | 			continue; | ||||||
| 		ResourcePath savePath(filename, EResType::IMAGE); | 		ResourcePath savePath(filename, EResType::IMAGE); | ||||||
|  |  | ||||||
| 		auto locator = ImageLocator(imgPathBg); | 		auto locator = ImageLocator(imgPathBg, EImageBlitMode::OPAQUE); | ||||||
| 		locator.scalingFactor = 1; |  | ||||||
|  |  | ||||||
| 		std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator, EImageBlitMode::OPAQUE); | 		std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator); | ||||||
| 		Canvas canvas = Canvas(Point(200, 116), CanvasScalingPolicy::IGNORE); | 		Canvas canvas = Canvas(Point(200, 116), CanvasScalingPolicy::IGNORE); | ||||||
| 		 | 		 | ||||||
| 		switch (i) | 		switch (i) | ||||||
| @@ -323,9 +317,8 @@ void AssetGenerator::createChroniclesCampaignImages() | |||||||
| 			canvas.draw(img, Point(0, 0), Rect(268, 210, 200, 116)); | 			canvas.draw(img, Point(0, 0), Rect(268, 210, 200, 116)); | ||||||
|  |  | ||||||
| 			//skull | 			//skull | ||||||
| 			auto locatorSkull = ImageLocator(ImagePath::builtin("CampSP1")); | 			auto locatorSkull = ImageLocator(ImagePath::builtin("CampSP1"), EImageBlitMode::OPAQUE); | ||||||
| 			locatorSkull.scalingFactor = 1; | 			std::shared_ptr<IImage> imgSkull = GH.renderHandler().loadImage(locatorSkull); | ||||||
| 			std::shared_ptr<IImage> imgSkull = GH.renderHandler().loadImage(locatorSkull, EImageBlitMode::OPAQUE); |  | ||||||
| 			canvas.draw(imgSkull, Point(162, 94), Rect(162, 94, 41, 22)); | 			canvas.draw(imgSkull, Point(162, 94), Rect(162, 94, 41, 22)); | ||||||
| 			canvas.draw(img, Point(162, 94), Rect(424, 304, 14, 4)); | 			canvas.draw(img, Point(162, 94), Rect(424, 304, 14, 4)); | ||||||
| 			canvas.draw(img, Point(162, 98), Rect(424, 308, 10, 4)); | 			canvas.draw(img, Point(162, 98), Rect(424, 308, 10, 4)); | ||||||
| @@ -403,8 +396,7 @@ void AssetGenerator::createPaletteShiftedSprites() | |||||||
| 					return; | 					return; | ||||||
|  |  | ||||||
| 				auto imgLoc = anim->getImageLocator(j, 0); | 				auto imgLoc = anim->getImageLocator(j, 0); | ||||||
| 				imgLoc.scalingFactor = 1; | 				auto img = GH.renderHandler().loadImage(imgLoc); | ||||||
| 				auto img = GH.renderHandler().loadImage(imgLoc, EImageBlitMode::COLORKEY); |  | ||||||
| 				for(int k = 0; k < paletteAnimations[i].size(); k++) | 				for(int k = 0; k < paletteAnimations[i].size(); k++) | ||||||
| 				{ | 				{ | ||||||
| 					auto element = paletteAnimations[i][k]; | 					auto element = paletteAnimations[i][k]; | ||||||
|   | |||||||
| @@ -30,7 +30,7 @@ bool CAnimation::loadFrame(size_t frame, size_t group, bool verbose) | |||||||
| 	if(auto image = getImageImpl(frame, group, false)) | 	if(auto image = getImageImpl(frame, group, false)) | ||||||
| 		return true; | 		return true; | ||||||
|  |  | ||||||
| 	std::shared_ptr<IImage> image = GH.renderHandler().loadImage(getImageLocator(frame, group), mode); | 	std::shared_ptr<IImage> image = GH.renderHandler().loadImage(getImageLocator(frame, group)); | ||||||
|  |  | ||||||
| 	if(image) | 	if(image) | ||||||
| 	{ | 	{ | ||||||
| @@ -224,5 +224,5 @@ ImageLocator CAnimation::getImageLocator(size_t frame, size_t group) const | |||||||
| 		throw std::runtime_error("Frame " + std::to_string(frame) + " of group " + std::to_string(group) + " is missing from animation " + name.getOriginalName() ); | 		throw std::runtime_error("Frame " + std::to_string(frame) + " of group " + std::to_string(group) + " is missing from animation " + name.getOriginalName() ); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return ImageLocator(name, frame, group); | 	return ImageLocator(name, frame, group, mode); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -106,7 +106,7 @@ void Canvas::draw(const std::shared_ptr<IImage>& image, const Point & pos) | |||||||
| { | { | ||||||
| 	assert(image); | 	assert(image); | ||||||
| 	if (image) | 	if (image) | ||||||
| 		image->draw(surface, transformPos(pos)); | 		image->draw(surface, transformPos(pos), nullptr, getScalingFactor()); | ||||||
| } | } | ||||||
|  |  | ||||||
| void Canvas::draw(const std::shared_ptr<IImage>& image, const Point & pos, const Rect & sourceRect) | void Canvas::draw(const std::shared_ptr<IImage>& image, const Point & pos, const Rect & sourceRect) | ||||||
| @@ -114,7 +114,7 @@ void Canvas::draw(const std::shared_ptr<IImage>& image, const Point & pos, const | |||||||
| 	Rect realSourceRect = sourceRect * getScalingFactor(); | 	Rect realSourceRect = sourceRect * getScalingFactor(); | ||||||
| 	assert(image); | 	assert(image); | ||||||
| 	if (image) | 	if (image) | ||||||
| 		image->draw(surface, transformPos(pos), &realSourceRect); | 		image->draw(surface, transformPos(pos), &realSourceRect, getScalingFactor()); | ||||||
| } | } | ||||||
|  |  | ||||||
| void Canvas::draw(const Canvas & image, const Point & pos) | void Canvas::draw(const Canvas & image, const Point & pos) | ||||||
| @@ -218,11 +218,11 @@ void Canvas::fillTexture(const std::shared_ptr<IImage>& image) | |||||||
| 	for (int y=0; y < surface->h; y+= imageArea.h) | 	for (int y=0; y < surface->h; y+= imageArea.h) | ||||||
| 	{ | 	{ | ||||||
| 		for (int x=0; x < surface->w; x+= imageArea.w) | 		for (int x=0; x < surface->w; x+= imageArea.w) | ||||||
| 			image->draw(surface, Point(renderArea.x + x * getScalingFactor(), renderArea.y + y * getScalingFactor())); | 			image->draw(surface, Point(renderArea.x + x * getScalingFactor(), renderArea.y + y * getScalingFactor()), nullptr, getScalingFactor()); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| SDL_Surface * Canvas::getInternalSurface() | SDL_Surface * Canvas::getInternalSurface() const | ||||||
| { | { | ||||||
| 	return surface; | 	return surface; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -115,7 +115,7 @@ public: | |||||||
| 	int getScalingFactor() const; | 	int getScalingFactor() const; | ||||||
|  |  | ||||||
| 	/// Compatibility method. AVOID USAGE. To be removed once SDL abstraction layer is finished. | 	/// Compatibility method. AVOID USAGE. To be removed once SDL abstraction layer is finished. | ||||||
| 	SDL_Surface * getInternalSurface(); | 	SDL_Surface * getInternalSurface() const; | ||||||
|  |  | ||||||
| 	/// get the render area | 	/// get the render area | ||||||
| 	Rect getRenderArea() const; | 	Rect getRenderArea() const; | ||||||
|   | |||||||
| @@ -68,10 +68,9 @@ class IImage | |||||||
| { | { | ||||||
| public: | public: | ||||||
| 	//draws image on surface "where" at position | 	//draws image on surface "where" at position | ||||||
| 	virtual void draw(SDL_Surface * where, const Point & pos, const Rect * src = nullptr) const = 0; | 	virtual void draw(SDL_Surface * where, const Point & pos, const Rect * src, int scalingFactor) const = 0; | ||||||
|  |  | ||||||
| 	virtual void scaleTo(const Point & size) = 0; | 	virtual void scaleTo(const Point & size) = 0; | ||||||
| 	virtual void scaleInteger(int factor) = 0; |  | ||||||
|  |  | ||||||
| 	virtual void exportBitmap(const boost::filesystem::path & path) const = 0; | 	virtual void exportBitmap(const boost::filesystem::path & path) const = 0; | ||||||
|  |  | ||||||
| @@ -92,13 +91,10 @@ public: | |||||||
| 	virtual void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) = 0; | 	virtual void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) = 0; | ||||||
|  |  | ||||||
| 	virtual void setAlpha(uint8_t value) = 0; | 	virtual void setAlpha(uint8_t value) = 0; | ||||||
| 	virtual void setBlitMode(EImageBlitMode mode) = 0; |  | ||||||
|  |  | ||||||
| 	//only indexed bitmaps with 7 special colors | 	//only indexed bitmaps with 7 special colors | ||||||
| 	virtual void setOverlayColor(const ColorRGBA & color) = 0; | 	virtual void setOverlayColor(const ColorRGBA & color) = 0; | ||||||
|  |  | ||||||
| 	virtual std::shared_ptr<const ISharedImage> getSharedImage() const = 0; |  | ||||||
|  |  | ||||||
| 	virtual ~IImage() = default; | 	virtual ~IImage() = default; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -114,13 +110,13 @@ public: | |||||||
| 	virtual Rect contentRect() const = 0; | 	virtual Rect contentRect() const = 0; | ||||||
| 	virtual void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const = 0; | 	virtual void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const = 0; | ||||||
|  |  | ||||||
| 	[[nodiscard]] virtual std::shared_ptr<IImage> createImageReference(EImageBlitMode mode) const = 0; | 	virtual ~ISharedImage() = default; | ||||||
|  |  | ||||||
|  | 	virtual const SDL_Palette * getPalette() const = 0; | ||||||
|  |  | ||||||
| 	[[nodiscard]] virtual std::shared_ptr<const ISharedImage> horizontalFlip() const = 0; | 	[[nodiscard]] virtual std::shared_ptr<const ISharedImage> horizontalFlip() const = 0; | ||||||
| 	[[nodiscard]] virtual std::shared_ptr<const ISharedImage> verticalFlip() const = 0; | 	[[nodiscard]] virtual std::shared_ptr<const ISharedImage> verticalFlip() const = 0; | ||||||
| 	[[nodiscard]] virtual std::shared_ptr<const ISharedImage> scaleInteger(int factor, SDL_Palette * palette, EImageBlitMode blitMode) const = 0; | 	[[nodiscard]] virtual std::shared_ptr<const ISharedImage> scaleInteger(int factor, SDL_Palette * palette, EImageBlitMode blitMode) const = 0; | ||||||
| 	[[nodiscard]] virtual std::shared_ptr<const ISharedImage> scaleTo(const Point & size, SDL_Palette * palette) const = 0; | 	[[nodiscard]] virtual std::shared_ptr<const ISharedImage> scaleTo(const Point & size, SDL_Palette * palette) const = 0; | ||||||
|  |  | ||||||
|  |  | ||||||
| 	virtual ~ISharedImage() = default; |  | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -20,6 +20,7 @@ struct SDL_Surface; | |||||||
| class IFont; | class IFont; | ||||||
| class IImage; | class IImage; | ||||||
| class CAnimation; | class CAnimation; | ||||||
|  | class SDLImageShared; | ||||||
| enum class EImageBlitMode : uint8_t; | enum class EImageBlitMode : uint8_t; | ||||||
| enum EFonts : int8_t; | enum EFonts : int8_t; | ||||||
|  |  | ||||||
| @@ -32,10 +33,13 @@ public: | |||||||
| 	virtual void onLibraryLoadingFinished(const Services * services) = 0; | 	virtual void onLibraryLoadingFinished(const Services * services) = 0; | ||||||
|  |  | ||||||
| 	/// Loads image using given path | 	/// Loads image using given path | ||||||
| 	virtual std::shared_ptr<IImage> loadImage(const ImageLocator & locator, EImageBlitMode mode) = 0; | 	virtual std::shared_ptr<IImage> loadImage(const ImageLocator & locator) = 0; | ||||||
| 	virtual std::shared_ptr<IImage> loadImage(const ImagePath & path, EImageBlitMode mode) = 0; | 	virtual std::shared_ptr<IImage> loadImage(const ImagePath & path, EImageBlitMode mode) = 0; | ||||||
| 	virtual std::shared_ptr<IImage> loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode) = 0; | 	virtual std::shared_ptr<IImage> loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode) = 0; | ||||||
|  |  | ||||||
|  | 	/// Loads single image without scaling support | ||||||
|  | 	virtual std::shared_ptr<SDLImageShared> loadSingleImage(const ImageLocator & locator) = 0; | ||||||
|  |  | ||||||
| 	/// temporary compatibility method. Creates IImage from existing SDL_Surface | 	/// temporary compatibility method. Creates IImage from existing SDL_Surface | ||||||
| 	/// Surface will be shared, caller must still free it with SDL_FreeSurface | 	/// Surface will be shared, caller must still free it with SDL_FreeSurface | ||||||
| 	virtual std::shared_ptr<IImage> createImage(SDL_Surface * source) = 0; | 	virtual std::shared_ptr<IImage> createImage(SDL_Surface * source) = 0; | ||||||
|   | |||||||
| @@ -15,11 +15,10 @@ | |||||||
|  |  | ||||||
| #include "../../lib/json/JsonNode.h" | #include "../../lib/json/JsonNode.h" | ||||||
|  |  | ||||||
| ImageLocator::ImageLocator(const JsonNode & config) | SharedImageLocator::SharedImageLocator(const JsonNode & config, EImageBlitMode mode) | ||||||
| 	: defFrame(config["defFrame"].Integer()) | 	: defFrame(config["defFrame"].Integer()) | ||||||
| 	, defGroup(config["defGroup"].Integer()) | 	, defGroup(config["defGroup"].Integer()) | ||||||
| 	, verticalFlip(config["verticalFlip"].Bool()) | 	, layer(mode) | ||||||
| 	, horizontalFlip(config["horizontalFlip"].Bool()) |  | ||||||
| { | { | ||||||
| 	if(!config["file"].isNull()) | 	if(!config["file"].isNull()) | ||||||
| 		image = ImagePath::fromJson(config["file"]); | 		image = ImagePath::fromJson(config["file"]); | ||||||
| @@ -28,19 +27,28 @@ ImageLocator::ImageLocator(const JsonNode & config) | |||||||
| 		defFile = AnimationPath::fromJson(config["defFile"]); | 		defFile = AnimationPath::fromJson(config["defFile"]); | ||||||
| } | } | ||||||
|  |  | ||||||
| ImageLocator::ImageLocator(const ImagePath & path) | SharedImageLocator::SharedImageLocator(const ImagePath & path, EImageBlitMode mode) | ||||||
| 	: image(path) | 	: image(path) | ||||||
|  | 	, layer(mode) | ||||||
| { | { | ||||||
| } | } | ||||||
|  |  | ||||||
| ImageLocator::ImageLocator(const AnimationPath & path, int frame, int group) | SharedImageLocator::SharedImageLocator(const AnimationPath & path, int frame, int group, EImageBlitMode mode) | ||||||
| 	: defFile(path) | 	: defFile(path) | ||||||
| 	, defFrame(frame) | 	, defFrame(frame) | ||||||
| 	, defGroup(group) | 	, defGroup(group) | ||||||
|  | 	, layer(mode) | ||||||
| { | { | ||||||
| } | } | ||||||
|  |  | ||||||
| bool ImageLocator::operator<(const ImageLocator & other) const | ImageLocator::ImageLocator(const JsonNode & config, EImageBlitMode mode) | ||||||
|  | 	: SharedImageLocator(config, mode) | ||||||
|  | 	, verticalFlip(config["verticalFlip"].Bool()) | ||||||
|  | 	, horizontalFlip(config["horizontalFlip"].Bool()) | ||||||
|  | { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool SharedImageLocator::operator < (const SharedImageLocator & other) const | ||||||
| { | { | ||||||
| 	if(image != other.image) | 	if(image != other.image) | ||||||
| 		return image < other.image; | 		return image < other.image; | ||||||
| @@ -50,14 +58,6 @@ bool ImageLocator::operator<(const ImageLocator & other) const | |||||||
| 		return defGroup < other.defGroup; | 		return defGroup < other.defGroup; | ||||||
| 	if(defFrame != other.defFrame) | 	if(defFrame != other.defFrame) | ||||||
| 		return defFrame < other.defFrame; | 		return defFrame < other.defFrame; | ||||||
| 	if(verticalFlip != other.verticalFlip) |  | ||||||
| 		return verticalFlip < other.verticalFlip; |  | ||||||
| 	if(horizontalFlip != other.horizontalFlip) |  | ||||||
| 		return horizontalFlip < other.horizontalFlip; |  | ||||||
| 	if(scalingFactor != other.scalingFactor) |  | ||||||
| 		return scalingFactor < other.scalingFactor; |  | ||||||
| 	if(playerColored != other.playerColored) |  | ||||||
| 		return playerColored < other.playerColored; |  | ||||||
| 	if(layer != other.layer) | 	if(layer != other.layer) | ||||||
| 		return layer < other.layer; | 		return layer < other.layer; | ||||||
|  |  | ||||||
| @@ -68,70 +68,3 @@ bool ImageLocator::empty() const | |||||||
| { | { | ||||||
| 	return !image.has_value() && !defFile.has_value(); | 	return !image.has_value() && !defFile.has_value(); | ||||||
| } | } | ||||||
|  |  | ||||||
| ImageLocator ImageLocator::copyFile() const |  | ||||||
| { |  | ||||||
| 	ImageLocator result; |  | ||||||
| 	result.scalingFactor = 1; |  | ||||||
| 	result.preScaledFactor = preScaledFactor; |  | ||||||
| 	result.image = image; |  | ||||||
| 	result.defFile = defFile; |  | ||||||
| 	result.defFrame = defFrame; |  | ||||||
| 	result.defGroup = defGroup; |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| ImageLocator ImageLocator::copyFileTransform() const |  | ||||||
| { |  | ||||||
| 	ImageLocator result = copyFile(); |  | ||||||
| 	result.horizontalFlip = horizontalFlip; |  | ||||||
| 	result.verticalFlip = verticalFlip; |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| ImageLocator ImageLocator::copyFileTransformScale() const |  | ||||||
| { |  | ||||||
| 	return *this; // full copy |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::string ImageLocator::toString() const |  | ||||||
| { |  | ||||||
| 	std::string result; |  | ||||||
| 	if (empty()) |  | ||||||
| 		return "invalid"; |  | ||||||
|  |  | ||||||
| 	if (image) |  | ||||||
| 	{ |  | ||||||
| 		result += image->getOriginalName(); |  | ||||||
| 		assert(!result.empty()); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (defFile) |  | ||||||
| 	{ |  | ||||||
| 		result += defFile->getOriginalName(); |  | ||||||
| 		assert(!result.empty()); |  | ||||||
| 		result += "-" + std::to_string(defGroup); |  | ||||||
| 		result += "-" + std::to_string(defFrame); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (verticalFlip) |  | ||||||
| 		result += "-vflip"; |  | ||||||
|  |  | ||||||
| 	if (horizontalFlip) |  | ||||||
| 		result += "-hflip"; |  | ||||||
|  |  | ||||||
| 	if (scalingFactor > 1) |  | ||||||
| 		result += "-scale" + std::to_string(scalingFactor); |  | ||||||
|  |  | ||||||
| 	if (playerColored.isValidPlayer()) |  | ||||||
| 		result += "-player" + playerColored.toString(); |  | ||||||
|  |  | ||||||
| 	if (layer == EImageBlitMode::ONLY_OVERLAY) |  | ||||||
| 		result += "-overlay"; |  | ||||||
|  |  | ||||||
| 	if (layer == EImageBlitMode::ONLY_SHADOW) |  | ||||||
| 		result += "-shadow"; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -14,35 +14,32 @@ | |||||||
| #include "../../lib/filesystem/ResourcePath.h" | #include "../../lib/filesystem/ResourcePath.h" | ||||||
| #include "../../lib/constants/EntityIdentifiers.h" | #include "../../lib/constants/EntityIdentifiers.h" | ||||||
|  |  | ||||||
| struct ImageLocator | struct SharedImageLocator | ||||||
| { | { | ||||||
| 	std::optional<ImagePath> image; | 	std::optional<ImagePath> image; | ||||||
| 	std::optional<AnimationPath> defFile; | 	std::optional<AnimationPath> defFile; | ||||||
| 	int defFrame = -1; | 	int defFrame = -1; | ||||||
| 	int defGroup = -1; | 	int defGroup = -1; | ||||||
|  | 	EImageBlitMode layer = EImageBlitMode::OPAQUE; | ||||||
|  |  | ||||||
| 	PlayerColor playerColored = PlayerColor::CANNOT_DETERMINE; // FIXME: treat as identical to blue to avoid double-loading? | 	SharedImageLocator() = default; | ||||||
|  | 	SharedImageLocator(const AnimationPath & path, int frame, int group, EImageBlitMode layer); | ||||||
|  | 	SharedImageLocator(const JsonNode & config, EImageBlitMode layer); | ||||||
|  | 	SharedImageLocator(const ImagePath & path, EImageBlitMode layer); | ||||||
|  |  | ||||||
|  | 	bool operator < (const SharedImageLocator & other) const; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct ImageLocator : SharedImageLocator | ||||||
|  | { | ||||||
|  | 	PlayerColor playerColored = PlayerColor::CANNOT_DETERMINE; | ||||||
|  |  | ||||||
| 	bool verticalFlip = false; | 	bool verticalFlip = false; | ||||||
| 	bool horizontalFlip = false; | 	bool horizontalFlip = false; | ||||||
| 	int8_t scalingFactor = 0; // 0 = auto / use default scaling | 	int8_t scalingFactor = 0; // 0 = auto / use default scaling | ||||||
| 	int8_t preScaledFactor = 1; |  | ||||||
| 	EImageBlitMode layer = EImageBlitMode::OPAQUE; |  | ||||||
|  |  | ||||||
| 	ImageLocator() = default; | 	using SharedImageLocator::SharedImageLocator; | ||||||
| 	ImageLocator(const AnimationPath & path, int frame, int group); | 	 ImageLocator(const JsonNode & config, EImageBlitMode layer); | ||||||
| 	explicit ImageLocator(const JsonNode & config); |  | ||||||
| 	explicit ImageLocator(const ImagePath & path); |  | ||||||
|  |  | ||||||
| 	bool operator < (const ImageLocator & other) const; |  | ||||||
| 	bool empty() const; | 	bool empty() const; | ||||||
|  |  | ||||||
| 	ImageLocator copyFile() const; |  | ||||||
| 	ImageLocator copyFileTransform() const; |  | ||||||
| 	ImageLocator copyFileTransformScale() const; |  | ||||||
|  |  | ||||||
| 	// generates string representation of this image locator |  | ||||||
| 	// guaranteed to be a valid file path with no extension |  | ||||||
| 	// but may contain '/' if source file is in directory |  | ||||||
| 	std::string toString() const; |  | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -59,7 +59,7 @@ void CursorHardware::setImage(std::shared_ptr<IImage> image, const Point & pivot | |||||||
|  |  | ||||||
| 	CSDL_Ext::fillSurface(cursorSurface, CSDL_Ext::toSDL(Colors::TRANSPARENCY)); | 	CSDL_Ext::fillSurface(cursorSurface, CSDL_Ext::toSDL(Colors::TRANSPARENCY)); | ||||||
|  |  | ||||||
| 	image->draw(cursorSurface, Point(0,0)); | 	image->draw(cursorSurface, Point(0,0), nullptr, GH.screenHandler().getScalingFactor()); | ||||||
| 	auto cursorSurfaceScaled = CSDL_Ext::scaleSurface(cursorSurface, cursorDimensionsScaled.x, cursorDimensionsScaled.y ); | 	auto cursorSurfaceScaled = CSDL_Ext::scaleSurface(cursorSurface, cursorDimensionsScaled.x, cursorDimensionsScaled.y ); | ||||||
|  |  | ||||||
| 	auto oldCursor = cursor; | 	auto oldCursor = cursor; | ||||||
|   | |||||||
| @@ -65,7 +65,7 @@ void CursorSoftware::updateTexture() | |||||||
|  |  | ||||||
| 	CSDL_Ext::fillSurface(cursorSurface, CSDL_Ext::toSDL(Colors::TRANSPARENCY)); | 	CSDL_Ext::fillSurface(cursorSurface, CSDL_Ext::toSDL(Colors::TRANSPARENCY)); | ||||||
|  |  | ||||||
| 	cursorImage->draw(cursorSurface, Point(0,0)); | 	cursorImage->draw(cursorSurface, Point(0,0), nullptr, GH.screenHandler().getScalingFactor()); | ||||||
| 	SDL_UpdateTexture(cursorTexture, nullptr, cursorSurface->pixels, cursorSurface->pitch); | 	SDL_UpdateTexture(cursorTexture, nullptr, cursorSurface->pixels, cursorSurface->pitch); | ||||||
| 	needUpdate = false; | 	needUpdate = false; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,172 +0,0 @@ | |||||||
| /* |  | ||||||
|  * ImageScaled.cpp, part of VCMI engine |  | ||||||
|  * |  | ||||||
|  * Authors: listed in file AUTHORS in main folder |  | ||||||
|  * |  | ||||||
|  * License: GNU General Public License v2.0 or later |  | ||||||
|  * Full text of license available in license.txt file, in main folder |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
| #include "StdInc.h" |  | ||||||
| #include "ImageScaled.h" |  | ||||||
|  |  | ||||||
| #include "SDLImage.h" |  | ||||||
| #include "SDL_Extensions.h" |  | ||||||
|  |  | ||||||
| #include "../gui/CGuiHandler.h" |  | ||||||
| #include "../render/IScreenHandler.h" |  | ||||||
| #include "../render/Colors.h" |  | ||||||
|  |  | ||||||
| #include "../../lib/constants/EntityIdentifiers.h" |  | ||||||
|  |  | ||||||
| #include <SDL_surface.h> |  | ||||||
|  |  | ||||||
| ImageScaled::ImageScaled(const ImageLocator & inputLocator, const std::shared_ptr<const ISharedImage> & source, EImageBlitMode mode) |  | ||||||
| 	: source(source) |  | ||||||
| 	, locator(inputLocator) |  | ||||||
| 	, colorMultiplier(Colors::WHITE_TRUE) |  | ||||||
| 	, alphaValue(SDL_ALPHA_OPAQUE) |  | ||||||
| 	, blitMode(mode) |  | ||||||
| { |  | ||||||
| 	prepareImages(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::shared_ptr<const ISharedImage> ImageScaled::getSharedImage() const |  | ||||||
| { |  | ||||||
| 	return body; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ImageScaled::scaleInteger(int factor) |  | ||||||
| { |  | ||||||
| 	assert(0); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ImageScaled::scaleTo(const Point & size) |  | ||||||
| { |  | ||||||
| 	if (source) |  | ||||||
| 		source = source->scaleTo(size, nullptr); |  | ||||||
|  |  | ||||||
| 	if (body) |  | ||||||
| 		body = body->scaleTo(size * GH.screenHandler().getScalingFactor(), nullptr); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ImageScaled::exportBitmap(const boost::filesystem::path &path) const |  | ||||||
| { |  | ||||||
| 	source->exportBitmap(path, nullptr); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool ImageScaled::isTransparent(const Point &coords) const |  | ||||||
| { |  | ||||||
| 	return source->isTransparent(coords); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Rect ImageScaled::contentRect() const |  | ||||||
| { |  | ||||||
| 	return source->contentRect(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Point ImageScaled::dimensions() const |  | ||||||
| { |  | ||||||
| 	return source->dimensions(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ImageScaled::setAlpha(uint8_t value) |  | ||||||
| { |  | ||||||
| 	alphaValue = value; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ImageScaled::setBlitMode(EImageBlitMode mode) |  | ||||||
| { |  | ||||||
| 	blitMode = mode; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ImageScaled::draw(SDL_Surface *where, const Point &pos, const Rect *src) const |  | ||||||
| { |  | ||||||
| 	if (shadow) |  | ||||||
| 		shadow->draw(where, nullptr, pos, src, Colors::WHITE_TRUE, alphaValue, blitMode); |  | ||||||
| 	if (body) |  | ||||||
| 		body->draw(where, nullptr, pos, src, Colors::WHITE_TRUE, alphaValue, blitMode); |  | ||||||
| 	if (overlay) |  | ||||||
| 		overlay->draw(where, nullptr, pos, src, colorMultiplier, colorMultiplier.a * alphaValue / 255, blitMode); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ImageScaled::setOverlayColor(const ColorRGBA & color) |  | ||||||
| { |  | ||||||
| 	colorMultiplier = color; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ImageScaled::playerColored(PlayerColor player) |  | ||||||
| { |  | ||||||
| 	playerColor = player; |  | ||||||
| 	prepareImages(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ImageScaled::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) |  | ||||||
| { |  | ||||||
| 	// TODO: implement |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ImageScaled::adjustPalette(const ColorFilter &shifter, uint32_t colorsToSkipMask) |  | ||||||
| { |  | ||||||
| 	// TODO: implement |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ImageScaled::prepareImages() |  | ||||||
| { |  | ||||||
| 	switch(blitMode) |  | ||||||
| 	{ |  | ||||||
| 		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; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	switch(blitMode) |  | ||||||
| 	{ |  | ||||||
| 		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; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	switch(blitMode) |  | ||||||
| 	{ |  | ||||||
| 		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; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,66 +0,0 @@ | |||||||
| /* |  | ||||||
|  * ImageScaled.h, part of VCMI engine |  | ||||||
|  * |  | ||||||
|  * Authors: listed in file AUTHORS in main folder |  | ||||||
|  * |  | ||||||
|  * License: GNU General Public License v2.0 or later |  | ||||||
|  * Full text of license available in license.txt file, in main folder |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
| #pragma once |  | ||||||
|  |  | ||||||
| #include "../render/IImage.h" |  | ||||||
| #include "../render/IRenderHandler.h" |  | ||||||
|  |  | ||||||
| #include "../../lib/Color.h" |  | ||||||
| #include "../../lib/constants/EntityIdentifiers.h" |  | ||||||
|  |  | ||||||
| struct SDL_Palette; |  | ||||||
|  |  | ||||||
| class SDLImageShared; |  | ||||||
|  |  | ||||||
| // Upscaled image with several mechanisms to emulate H3 palette effects |  | ||||||
| class ImageScaled final : public IImage |  | ||||||
| { |  | ||||||
| private: |  | ||||||
|  |  | ||||||
| 	/// Original unscaled image |  | ||||||
| 	std::shared_ptr<const ISharedImage> source; |  | ||||||
|  |  | ||||||
| 	/// Upscaled shadow of our image, may be null |  | ||||||
| 	std::shared_ptr<const ISharedImage> shadow; |  | ||||||
|  |  | ||||||
| 	/// Upscaled main part of our image, may be null |  | ||||||
| 	std::shared_ptr<const ISharedImage> body; |  | ||||||
|  |  | ||||||
| 	/// Upscaled overlay (player color, selection highlight) of our image, may be null |  | ||||||
| 	std::shared_ptr<const ISharedImage> overlay; |  | ||||||
|  |  | ||||||
| 	ImageLocator locator; |  | ||||||
|  |  | ||||||
| 	ColorRGBA colorMultiplier; |  | ||||||
| 	PlayerColor playerColor = PlayerColor::CANNOT_DETERMINE; |  | ||||||
|  |  | ||||||
| 	uint8_t alphaValue; |  | ||||||
| 	EImageBlitMode blitMode; |  | ||||||
|  |  | ||||||
| 	void prepareImages(); |  | ||||||
| public: |  | ||||||
| 	ImageScaled(const ImageLocator & locator, const std::shared_ptr<const ISharedImage> & source, EImageBlitMode mode); |  | ||||||
|  |  | ||||||
| 	void scaleInteger(int factor) override; |  | ||||||
| 	void scaleTo(const Point & size) override; |  | ||||||
| 	void exportBitmap(const boost::filesystem::path & path) const override; |  | ||||||
| 	bool isTransparent(const Point & coords) const override; |  | ||||||
| 	Rect contentRect() const override; |  | ||||||
| 	Point dimensions() const override; |  | ||||||
| 	void setAlpha(uint8_t value) override; |  | ||||||
| 	void setBlitMode(EImageBlitMode mode) override; |  | ||||||
| 	void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override; |  | ||||||
| 	void setOverlayColor(const ColorRGBA & color) override; |  | ||||||
| 	void playerColored(PlayerColor player) override; |  | ||||||
| 	void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; |  | ||||||
| 	void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; |  | ||||||
|  |  | ||||||
| 	std::shared_ptr<const ISharedImage> getSharedImage() const override; |  | ||||||
| }; |  | ||||||
| @@ -11,7 +11,7 @@ | |||||||
| #include "RenderHandler.h" | #include "RenderHandler.h" | ||||||
|  |  | ||||||
| #include "SDLImage.h" | #include "SDLImage.h" | ||||||
| #include "ImageScaled.h" | #include "ScalableImage.h" | ||||||
| #include "FontChain.h" | #include "FontChain.h" | ||||||
|  |  | ||||||
| #include "../gui/CGuiHandler.h" | #include "../gui/CGuiHandler.h" | ||||||
| @@ -22,6 +22,7 @@ | |||||||
| #include "../render/ColorFilter.h" | #include "../render/ColorFilter.h" | ||||||
| #include "../render/IScreenHandler.h" | #include "../render/IScreenHandler.h" | ||||||
| #include "../../lib/json/JsonUtils.h" | #include "../../lib/json/JsonUtils.h" | ||||||
|  | #include "../../lib/CThreadHelper.h" | ||||||
| #include "../../lib/filesystem/Filesystem.h" | #include "../../lib/filesystem/Filesystem.h" | ||||||
| #include "../../lib/VCMIDirs.h" | #include "../../lib/VCMIDirs.h" | ||||||
|  |  | ||||||
| @@ -55,60 +56,7 @@ std::shared_ptr<CDefFile> RenderHandler::getAnimationFile(const AnimationPath & | |||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|  |  | ||||||
| std::optional<ResourcePath> RenderHandler::getPathForScaleFactor(const ResourcePath & path, const std::string & factor) | void RenderHandler::initFromJson(AnimationLayoutMap & source, const JsonNode & config, EImageBlitMode mode) | ||||||
| { |  | ||||||
| 	if(path.getType() == EResType::IMAGE) |  | ||||||
| 	{ |  | ||||||
| 		auto p = ImagePath::builtin(path.getName()); |  | ||||||
| 		if(CResourceHandler::get()->existsResource(p.addPrefix("SPRITES" + factor + "X/"))) |  | ||||||
| 			return std::optional<ResourcePath>(p.addPrefix("SPRITES" + factor + "X/")); |  | ||||||
| 		if(CResourceHandler::get()->existsResource(p.addPrefix("DATA" + factor + "X/"))) |  | ||||||
| 			return std::optional<ResourcePath>(p.addPrefix("DATA" + factor + "X/")); |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 	{ |  | ||||||
| 		auto p = AnimationPath::builtin(path.getName()); |  | ||||||
| 		auto pJson = p.toType<EResType::JSON>(); |  | ||||||
| 		if(CResourceHandler::get()->existsResource(p.addPrefix("SPRITES" + factor + "X/"))) |  | ||||||
| 			return std::optional<ResourcePath>(p.addPrefix("SPRITES" + factor + "X/")); |  | ||||||
| 		if(CResourceHandler::get()->existsResource(pJson)) |  | ||||||
| 			return std::optional<ResourcePath>(p); |  | ||||||
| 		if(CResourceHandler::get()->existsResource(pJson.addPrefix("SPRITES" + factor + "X/"))) |  | ||||||
| 			return std::optional<ResourcePath>(p.addPrefix("SPRITES" + factor + "X/")); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return std::nullopt; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::pair<ResourcePath, int> RenderHandler::getScalePath(const ResourcePath & p) |  | ||||||
| { |  | ||||||
| 	auto path = p; |  | ||||||
| 	int scaleFactor = 1; |  | ||||||
| 	if(getScalingFactor() > 1) |  | ||||||
| 	{ |  | ||||||
| 		std::vector<int> factorsToCheck = {getScalingFactor(), 4, 3, 2}; |  | ||||||
| 		for(auto factorToCheck : factorsToCheck) |  | ||||||
| 		{ |  | ||||||
| 			std::string name = boost::algorithm::to_upper_copy(p.getName()); |  | ||||||
| 			boost::replace_all(name, "SPRITES/", std::string("SPRITES") + std::to_string(factorToCheck) + std::string("X/")); |  | ||||||
| 			boost::replace_all(name, "DATA/", std::string("DATA") + std::to_string(factorToCheck) + std::string("X/")); |  | ||||||
| 			ResourcePath scaledPath = ImagePath::builtin(name); |  | ||||||
| 			if(p.getType() != EResType::IMAGE) |  | ||||||
| 				scaledPath = AnimationPath::builtin(name); |  | ||||||
| 			auto tmpPath = getPathForScaleFactor(scaledPath, std::to_string(factorToCheck)); |  | ||||||
| 			if(tmpPath) |  | ||||||
| 			{ |  | ||||||
| 				path = *tmpPath; |  | ||||||
| 				scaleFactor = factorToCheck; |  | ||||||
| 				break; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return std::pair<ResourcePath, int>(path, scaleFactor); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| void RenderHandler::initFromJson(AnimationLayoutMap & source, const JsonNode & config) |  | ||||||
| { | { | ||||||
| 	std::string basepath; | 	std::string basepath; | ||||||
| 	basepath = config["basepath"].String(); | 	basepath = config["basepath"].String(); | ||||||
| @@ -128,7 +76,7 @@ void RenderHandler::initFromJson(AnimationLayoutMap & source, const JsonNode & c | |||||||
| 			JsonNode toAdd = frame; | 			JsonNode toAdd = frame; | ||||||
| 			JsonUtils::inherit(toAdd, base); | 			JsonUtils::inherit(toAdd, base); | ||||||
| 			toAdd["file"].String() = basepath + frame.String(); | 			toAdd["file"].String() = basepath + frame.String(); | ||||||
| 			source[groupID].emplace_back(toAdd); | 			source[groupID].emplace_back(toAdd, mode); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -149,15 +97,26 @@ void RenderHandler::initFromJson(AnimationLayoutMap & source, const JsonNode & c | |||||||
| 		if (toAdd.Struct().count("defFile")) | 		if (toAdd.Struct().count("defFile")) | ||||||
| 			toAdd["defFile"].String() = basepath + node["defFile"].String(); | 			toAdd["defFile"].String() = basepath + node["defFile"].String(); | ||||||
|  |  | ||||||
| 		source[group][frame] = ImageLocator(toAdd); | 		source[group][frame] = ImageLocator(toAdd, mode); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| RenderHandler::AnimationLayoutMap & RenderHandler::getAnimationLayout(const AnimationPath & path) | RenderHandler::AnimationLayoutMap & RenderHandler::getAnimationLayout(const AnimationPath & path, int scalingFactor, EImageBlitMode mode) | ||||||
| { | { | ||||||
| 	auto tmp = getScalePath(path); | 	static constexpr std::array scaledSpritesPath = { | ||||||
| 	auto animPath = AnimationPath::builtin(tmp.first.getName()); | 		"", // 0x | ||||||
| 	AnimationPath actualPath = boost::starts_with(animPath.getName(), "SPRITES") ? animPath : animPath.addPrefix("SPRITES/"); | 		"SPRITES/", | ||||||
|  | 		"SPRITES2X/", | ||||||
|  | 		"SPRITES3X/", | ||||||
|  | 		"SPRITES4X/", | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	std::string pathString = path.getName(); | ||||||
|  |  | ||||||
|  | 	if (boost::starts_with(pathString, "SPRITES/")) | ||||||
|  | 		pathString = pathString.substr(std::string("SPRITES/").length()); | ||||||
|  |  | ||||||
|  | 	AnimationPath actualPath = AnimationPath::builtin(scaledSpritesPath.at(scalingFactor) + pathString); | ||||||
|  |  | ||||||
| 	auto it = animationLayouts.find(actualPath); | 	auto it = animationLayouts.find(actualPath); | ||||||
|  |  | ||||||
| @@ -184,15 +143,11 @@ RenderHandler::AnimationLayoutMap & RenderHandler::getAnimationLayout(const Anim | |||||||
| 		std::unique_ptr<ui8[]> textData(new ui8[stream->getSize()]); | 		std::unique_ptr<ui8[]> textData(new ui8[stream->getSize()]); | ||||||
| 		stream->read(textData.get(), stream->getSize()); | 		stream->read(textData.get(), stream->getSize()); | ||||||
|  |  | ||||||
| 		const JsonNode config(reinterpret_cast<const std::byte*>(textData.get()), stream->getSize(), animPath.getOriginalName()); | 		const JsonNode config(reinterpret_cast<const std::byte*>(textData.get()), stream->getSize(), path.getOriginalName()); | ||||||
|  |  | ||||||
| 		initFromJson(result, config); | 		initFromJson(result, config, mode); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for(auto & g : result) |  | ||||||
| 		for(auto & i : g.second) |  | ||||||
| 			i.preScaledFactor = tmp.second; |  | ||||||
|  |  | ||||||
| 	animationLayouts[actualPath] = result; | 	animationLayouts[actualPath] = result; | ||||||
| 	return animationLayouts[actualPath]; | 	return animationLayouts[actualPath]; | ||||||
| } | } | ||||||
| @@ -202,209 +157,170 @@ int RenderHandler::getScalingFactor() const | |||||||
| 	return GH.screenHandler().getScalingFactor(); | 	return GH.screenHandler().getScalingFactor(); | ||||||
| } | } | ||||||
|  |  | ||||||
| ImageLocator RenderHandler::getLocatorForAnimationFrame(const AnimationPath & path, int frame, int group) | ImageLocator RenderHandler::getLocatorForAnimationFrame(const AnimationPath & path, int frame, int group, EImageBlitMode mode) | ||||||
| { | { | ||||||
| 	const auto & layout = getAnimationLayout(path); | 	const auto & layout = getAnimationLayout(path, 1, mode); | ||||||
| 	if (!layout.count(group)) | 	if (!layout.count(group)) | ||||||
| 		return ImageLocator(ImagePath::builtin("DEFAULT")); | 		return ImageLocator(ImagePath::builtin("DEFAULT"), mode); | ||||||
|  |  | ||||||
| 	if (frame >= layout.at(group).size()) | 	if (frame >= layout.at(group).size()) | ||||||
| 		return ImageLocator(ImagePath::builtin("DEFAULT")); | 		return ImageLocator(ImagePath::builtin("DEFAULT"), mode); | ||||||
|  |  | ||||||
| 	const auto & locator = layout.at(group).at(frame); | 	const auto & locator = layout.at(group).at(frame); | ||||||
| 	if (locator.image || locator.defFile) | 	if (locator.image || locator.defFile) | ||||||
| 		return locator; | 		return locator; | ||||||
|  |  | ||||||
| 	return ImageLocator(path, frame, group); | 	return ImageLocator(path, frame, group, mode); | ||||||
| } | } | ||||||
|  |  | ||||||
| std::shared_ptr<const ISharedImage> RenderHandler::loadImageImpl(const ImageLocator & locator) | std::shared_ptr<ScalableImageShared> RenderHandler::loadImageImpl(const ImageLocator & locator) | ||||||
| { | { | ||||||
| 	auto it = imageFiles.find(locator); | 	auto it = imageFiles.find(locator); | ||||||
| 	if (it != imageFiles.end()) | 	if (it != imageFiles.end()) | ||||||
| 		return it->second; | 		return it->second; | ||||||
|  |  | ||||||
| 	// TODO: order should be different: | 	auto sdlImage = loadImageFromFileUncached(locator); | ||||||
| 	// 1) try to find correctly scaled image | 	auto scaledImage = std::make_shared<ScalableImageShared>(locator, sdlImage); | ||||||
| 	// 2) if fails -> try to find correctly transformed |  | ||||||
| 	// 3) if also fails -> try to find image from correct file |  | ||||||
| 	// 4) load missing part of the sequence |  | ||||||
| 	// TODO: check whether (load -> transform -> scale) or (load -> scale -> transform) order should be used for proper loading of pre-scaled data |  | ||||||
| 	auto imageFromFile = loadImageFromFile(locator.copyFile()); |  | ||||||
| 	auto transformedImage = transformImage(locator.copyFileTransform(), imageFromFile); |  | ||||||
| 	auto scaledImage = scaleImage(locator.copyFileTransformScale(), transformedImage); |  | ||||||
|  |  | ||||||
|  | 	storeCachedImage(locator, scaledImage); | ||||||
| 	return scaledImage; | 	return scaledImage; | ||||||
| } | } | ||||||
|  |  | ||||||
| std::shared_ptr<const ISharedImage> RenderHandler::loadImageFromFileUncached(const ImageLocator & locator) | std::shared_ptr<SDLImageShared> RenderHandler::loadImageFromFileUncached(const ImageLocator & locator) | ||||||
| { | { | ||||||
| 	if(locator.image) | 	if(locator.image) | ||||||
| 	{ | 	{ | ||||||
| 		// TODO: create EmptySharedImage class that will be instantiated if image does not exists or fails to load | 		// TODO: create EmptySharedImage class that will be instantiated if image does not exists or fails to load | ||||||
| 		return std::make_shared<SDLImageShared>(*locator.image, locator.preScaledFactor); | 		return std::make_shared<SDLImageShared>(*locator.image); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if(locator.defFile) | 	if(locator.defFile) | ||||||
| 	{ | 	{ | ||||||
| 		auto defFile = getAnimationFile(*locator.defFile); | 		auto defFile = getAnimationFile(*locator.defFile); | ||||||
| 		int preScaledFactor = locator.preScaledFactor; |  | ||||||
| 		if(!defFile) // no prescale for this frame |  | ||||||
| 		{ |  | ||||||
| 			auto tmpPath = (*locator.defFile).getName(); |  | ||||||
| 			boost::algorithm::replace_all(tmpPath, "SPRITES2X/", "SPRITES/"); |  | ||||||
| 			boost::algorithm::replace_all(tmpPath, "SPRITES3X/", "SPRITES/"); |  | ||||||
| 			boost::algorithm::replace_all(tmpPath, "SPRITES4X/", "SPRITES/"); |  | ||||||
| 			preScaledFactor = 1; |  | ||||||
| 			defFile = getAnimationFile(AnimationPath::builtin(tmpPath)); |  | ||||||
| 		} |  | ||||||
| 		if(defFile->hasFrame(locator.defFrame, locator.defGroup)) | 		if(defFile->hasFrame(locator.defFrame, locator.defGroup)) | ||||||
| 			return std::make_shared<SDLImageShared>(defFile.get(), locator.defFrame, locator.defGroup, preScaledFactor); | 			return std::make_shared<SDLImageShared>(defFile.get(), locator.defFrame, locator.defGroup); | ||||||
| 		else | 		else | ||||||
| 		{ | 		{ | ||||||
| 			logGlobal->error("Frame %d in group %d not found in file: %s",  | 			logGlobal->error("Frame %d in group %d not found in file: %s",  | ||||||
| 				locator.defFrame, locator.defGroup, locator.defFile->getName().c_str()); | 				locator.defFrame, locator.defGroup, locator.defFile->getName().c_str()); | ||||||
| 			return std::make_shared<SDLImageShared>(ImagePath::builtin("DEFAULT"), locator.preScaledFactor); | 			return std::make_shared<SDLImageShared>(ImagePath::builtin("DEFAULT")); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	throw std::runtime_error("Invalid image locator received!"); | 	throw std::runtime_error("Invalid image locator received!"); | ||||||
| } | } | ||||||
|  |  | ||||||
| void RenderHandler::storeCachedImage(const ImageLocator & locator, std::shared_ptr<const ISharedImage> image) | void RenderHandler::storeCachedImage(const ImageLocator & locator, std::shared_ptr<ScalableImageShared> image) | ||||||
| { | { | ||||||
| 	imageFiles[locator] = image; | 	imageFiles[locator] = image; | ||||||
|  |  | ||||||
| #if 0 |  | ||||||
| 	const boost::filesystem::path outPath = VCMIDirs::get().userExtractedPath() / "imageCache" / (locator.toString() + ".png"); |  | ||||||
| 	boost::filesystem::path outDir = outPath; |  | ||||||
| 	outDir.remove_filename(); |  | ||||||
| 	boost::filesystem::create_directories(outDir); |  | ||||||
| 	image->exportBitmap(outPath , nullptr); |  | ||||||
| #endif |  | ||||||
| } | } | ||||||
|  |  | ||||||
| std::shared_ptr<const ISharedImage> RenderHandler::loadImageFromFile(const ImageLocator & locator) | std::shared_ptr<SDLImageShared> RenderHandler::loadSingleImage(const ImageLocator & locator) | ||||||
| { | { | ||||||
| 	if (imageFiles.count(locator)) | 	assert(locator.scalingFactor != 0); | ||||||
| 		return imageFiles.at(locator); |  | ||||||
|  |  | ||||||
| 	auto result = loadImageFromFileUncached(locator); | 	static constexpr std::array scaledDataPath = { | ||||||
| 	storeCachedImage(locator, result); | 		"", // 0x | ||||||
| 	return result; | 		"DATA/", | ||||||
|  | 		"DATA2X/", | ||||||
|  | 		"DATA3X/", | ||||||
|  | 		"DATA4X/", | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	static constexpr std::array scaledSpritesPath = { | ||||||
|  | 		"", // 0x | ||||||
|  | 		"SPRITES/", | ||||||
|  | 		"SPRITES2X/", | ||||||
|  | 		"SPRITES3X/", | ||||||
|  | 		"SPRITES4X/", | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	if(locator.image) | ||||||
|  | 	{ | ||||||
|  | 		std::string imagePathString = locator.image->getName(); | ||||||
|  |  | ||||||
|  | 		if(locator.layer == EImageBlitMode::ONLY_OVERLAY) | ||||||
|  | 			imagePathString += "-OVERLAY"; | ||||||
|  | 		if(locator.layer == EImageBlitMode::ONLY_SHADOW) | ||||||
|  | 			imagePathString += "-SHADOW"; | ||||||
|  |  | ||||||
|  | 		auto imagePath = ImagePath::builtin(imagePathString); | ||||||
|  | 		auto imagePathSprites = ImagePath::builtin(imagePathString).addPrefix(scaledSpritesPath.at(locator.scalingFactor)); | ||||||
|  | 		auto imagePathData = ImagePath::builtin(imagePathString).addPrefix(scaledDataPath.at(locator.scalingFactor)); | ||||||
|  |  | ||||||
|  | 		if(CResourceHandler::get()->existsResource(imagePathSprites)) | ||||||
|  | 			return std::make_shared<SDLImageShared>(imagePathSprites); | ||||||
|  |  | ||||||
|  | 		if(CResourceHandler::get()->existsResource(imagePathData)) | ||||||
|  | 			return std::make_shared<SDLImageShared>(imagePathData); | ||||||
|  |  | ||||||
|  | 		if(CResourceHandler::get()->existsResource(imagePath)) | ||||||
|  | 			return std::make_shared<SDLImageShared>(imagePath); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if(locator.defFile) | ||||||
|  | 	{ | ||||||
|  | 		AnimationPath defFilePath = locator.defFile->addPrefix(scaledSpritesPath.at(locator.scalingFactor)); | ||||||
|  | 		auto defFile = getAnimationFile(defFilePath); | ||||||
|  |  | ||||||
|  | 		if(defFile && defFile->hasFrame(locator.defFrame, locator.defGroup)) | ||||||
|  | 		{ | ||||||
|  | 			return std::make_shared<SDLImageShared>(defFile.get(), locator.defFrame, locator.defGroup); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return nullptr; | ||||||
| } | } | ||||||
|  |  | ||||||
| std::shared_ptr<const ISharedImage> RenderHandler::transformImage(const ImageLocator & locator, std::shared_ptr<const ISharedImage> image) | std::shared_ptr<IImage> RenderHandler::loadImage(const ImageLocator & locator) | ||||||
| { |  | ||||||
| 	if (imageFiles.count(locator)) |  | ||||||
| 		return imageFiles.at(locator); |  | ||||||
|  |  | ||||||
| 	auto result = image; |  | ||||||
|  |  | ||||||
| 	if (locator.verticalFlip) |  | ||||||
| 		result = result->verticalFlip(); |  | ||||||
|  |  | ||||||
| 	if (locator.horizontalFlip) |  | ||||||
| 		result = result->horizontalFlip(); |  | ||||||
|  |  | ||||||
| 	storeCachedImage(locator, result); |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::shared_ptr<const ISharedImage> RenderHandler::scaleImage(const ImageLocator & locator, std::shared_ptr<const ISharedImage> image) |  | ||||||
| { |  | ||||||
| 	if (imageFiles.count(locator)) |  | ||||||
| 		return imageFiles.at(locator); |  | ||||||
|  |  | ||||||
| 	auto handle = image->createImageReference(locator.layer); |  | ||||||
|  |  | ||||||
| 	assert(locator.scalingFactor != 1); // should be filtered-out before |  | ||||||
| 	if (locator.playerColored != PlayerColor::CANNOT_DETERMINE) |  | ||||||
| 		handle->playerColored(locator.playerColored); |  | ||||||
|  |  | ||||||
| 	handle->scaleInteger(locator.scalingFactor); |  | ||||||
|  |  | ||||||
| 	auto result = handle->getSharedImage(); |  | ||||||
| 	storeCachedImage(locator, result); |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::shared_ptr<IImage> RenderHandler::loadImage(const ImageLocator & locator, EImageBlitMode mode) |  | ||||||
| { | { | ||||||
| 	ImageLocator adjustedLocator = locator; | 	ImageLocator adjustedLocator = locator; | ||||||
|  |  | ||||||
| 	if(adjustedLocator.image) | 	std::shared_ptr<ScalableImageInstance> result; | ||||||
| 	{ |  | ||||||
| 		std::string imgPath = (*adjustedLocator.image).getName(); |  | ||||||
| 		if(adjustedLocator.layer == EImageBlitMode::ONLY_OVERLAY) |  | ||||||
| 			imgPath += "-OVERLAY"; |  | ||||||
| 		if(adjustedLocator.layer == EImageBlitMode::ONLY_SHADOW) |  | ||||||
| 			imgPath += "-SHADOW"; |  | ||||||
|  |  | ||||||
| 		if(CResourceHandler::get()->existsResource(ImagePath::builtin(imgPath)) || |  | ||||||
| 		   CResourceHandler::get()->existsResource(ImagePath::builtin(imgPath).addPrefix("DATA/")) || |  | ||||||
| 		   CResourceHandler::get()->existsResource(ImagePath::builtin(imgPath).addPrefix("SPRITES/"))) |  | ||||||
| 			adjustedLocator.image = ImagePath::builtin(imgPath); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if(adjustedLocator.defFile && adjustedLocator.scalingFactor == 0) |  | ||||||
| 	{ |  | ||||||
| 		auto tmp = getScalePath(*adjustedLocator.defFile); |  | ||||||
| 		adjustedLocator.defFile = AnimationPath::builtin(tmp.first.getName()); |  | ||||||
| 		adjustedLocator.preScaledFactor = tmp.second; |  | ||||||
| 	} |  | ||||||
| 	if(adjustedLocator.image && adjustedLocator.scalingFactor == 0) |  | ||||||
| 	{ |  | ||||||
| 		auto tmp = getScalePath(*adjustedLocator.image); |  | ||||||
| 		adjustedLocator.image = ImagePath::builtin(tmp.first.getName()); |  | ||||||
| 		adjustedLocator.preScaledFactor = tmp.second; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (adjustedLocator.scalingFactor == 0 && getScalingFactor() != 1 ) |  | ||||||
| 	{ |  | ||||||
| 		auto unscaledLocator = adjustedLocator; |  | ||||||
| 		auto scaledLocator = adjustedLocator; |  | ||||||
|  |  | ||||||
| 		unscaledLocator.scalingFactor = 1; |  | ||||||
| 		scaledLocator.scalingFactor = getScalingFactor(); |  | ||||||
| 		auto unscaledImage = loadImageImpl(unscaledLocator); |  | ||||||
|  |  | ||||||
| 		return std::make_shared<ImageScaled>(scaledLocator, unscaledImage, mode); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (adjustedLocator.scalingFactor == 0) | 	if (adjustedLocator.scalingFactor == 0) | ||||||
| 	{ | 	{ | ||||||
| 		auto scaledLocator = adjustedLocator; | 		auto scaledLocator = adjustedLocator; | ||||||
| 		scaledLocator.scalingFactor = getScalingFactor(); | 		scaledLocator.scalingFactor = getScalingFactor(); | ||||||
|  |  | ||||||
| 		return loadImageImpl(scaledLocator)->createImageReference(mode); | 		result = loadImageImpl(scaledLocator)->createImageReference(); | ||||||
| 	} | 	} | ||||||
| 	else | 	else | ||||||
| 		return loadImageImpl(adjustedLocator)->createImageReference(mode); | 		result = loadImageImpl(adjustedLocator)->createImageReference(); | ||||||
|  |  | ||||||
|  | 	if (locator.horizontalFlip) | ||||||
|  | 		result->horizontalFlip(); | ||||||
|  | 	if (locator.verticalFlip) | ||||||
|  | 		result->verticalFlip(); | ||||||
|  |  | ||||||
|  | 	return result; | ||||||
| } | } | ||||||
|  |  | ||||||
| std::shared_ptr<IImage> RenderHandler::loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode) | std::shared_ptr<IImage> RenderHandler::loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode) | ||||||
| { | { | ||||||
| 	auto tmp = getScalePath(path); | 	ImageLocator locator = getLocatorForAnimationFrame(path, frame, group, mode); | ||||||
| 	ImageLocator locator = getLocatorForAnimationFrame(AnimationPath::builtin(tmp.first.getName()), frame, group); | 	return loadImage(locator); | ||||||
| 	locator.preScaledFactor = tmp.second; |  | ||||||
| 	return loadImage(locator, mode); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| std::shared_ptr<IImage> RenderHandler::loadImage(const ImagePath & path, EImageBlitMode mode) | std::shared_ptr<IImage> RenderHandler::loadImage(const ImagePath & path, EImageBlitMode mode) | ||||||
| { | { | ||||||
| 	ImageLocator locator(path); | 	ImageLocator locator(path, mode); | ||||||
| 	return loadImage(locator, mode); | 	return loadImage(locator); | ||||||
| } | } | ||||||
|  |  | ||||||
| std::shared_ptr<IImage> RenderHandler::createImage(SDL_Surface * source) | std::shared_ptr<IImage> RenderHandler::createImage(SDL_Surface * source) | ||||||
| { | { | ||||||
| 	return std::make_shared<SDLImageShared>(source)->createImageReference(EImageBlitMode::SIMPLE); | 	auto baseImage = std::make_shared<SDLImageShared>(source); | ||||||
|  | 	SharedImageLocator locator; | ||||||
|  | 	locator.layer = EImageBlitMode::SIMPLE; | ||||||
|  | 	auto scalableImage = std::make_shared<ScalableImageShared>(locator, baseImage); | ||||||
|  |  | ||||||
|  | 	return scalableImage->createImageReference(); | ||||||
| } | } | ||||||
|  |  | ||||||
| std::shared_ptr<CAnimation> RenderHandler::loadAnimation(const AnimationPath & path, EImageBlitMode mode) | std::shared_ptr<CAnimation> RenderHandler::loadAnimation(const AnimationPath & path, EImageBlitMode mode) | ||||||
| { | { | ||||||
| 	return std::make_shared<CAnimation>(path, getAnimationLayout(path), mode); | 	return std::make_shared<CAnimation>(path, getAnimationLayout(path, 1, mode), mode); | ||||||
| } | } | ||||||
|  |  | ||||||
| void RenderHandler::addImageListEntries(const EntityService * service) | void RenderHandler::addImageListEntries(const EntityService * service) | ||||||
| @@ -416,7 +332,7 @@ void RenderHandler::addImageListEntries(const EntityService * service) | |||||||
| 			if (imageName.empty()) | 			if (imageName.empty()) | ||||||
| 				return; | 				return; | ||||||
|  |  | ||||||
| 			auto & layout = getAnimationLayout(AnimationPath::builtin("SPRITES/" + listName)); | 			auto & layout = getAnimationLayout(AnimationPath::builtin("SPRITES/" + listName), 1, EImageBlitMode::SIMPLE); | ||||||
|  |  | ||||||
| 			JsonNode entry; | 			JsonNode entry; | ||||||
| 			entry["file"].String() = imageName; | 			entry["file"].String() = imageName; | ||||||
| @@ -424,7 +340,7 @@ void RenderHandler::addImageListEntries(const EntityService * service) | |||||||
| 			if (index >= layout[group].size()) | 			if (index >= layout[group].size()) | ||||||
| 				layout[group].resize(index + 1); | 				layout[group].resize(index + 1); | ||||||
|  |  | ||||||
| 			layout[group][index] = ImageLocator(entry); | 			layout[group][index] = ImageLocator(entry, EImageBlitMode::SIMPLE); | ||||||
| 		}); | 		}); | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ VCMI_LIB_NAMESPACE_END | |||||||
|  |  | ||||||
| class CDefFile; | class CDefFile; | ||||||
| class SDLImageShared; | class SDLImageShared; | ||||||
| class ISharedImage; | class ScalableImageShared; | ||||||
|  |  | ||||||
| class RenderHandler : public IRenderHandler | class RenderHandler : public IRenderHandler | ||||||
| { | { | ||||||
| @@ -25,28 +25,22 @@ class RenderHandler : public IRenderHandler | |||||||
|  |  | ||||||
| 	std::map<AnimationPath, std::shared_ptr<CDefFile>> animationFiles; | 	std::map<AnimationPath, std::shared_ptr<CDefFile>> animationFiles; | ||||||
| 	std::map<AnimationPath, AnimationLayoutMap> animationLayouts; | 	std::map<AnimationPath, AnimationLayoutMap> animationLayouts; | ||||||
| 	std::map<ImageLocator, std::shared_ptr<const ISharedImage>> imageFiles; | 	std::map<SharedImageLocator, std::shared_ptr<ScalableImageShared>> imageFiles; | ||||||
| 	std::map<EFonts, std::shared_ptr<const IFont>> fonts; | 	std::map<EFonts, std::shared_ptr<const IFont>> fonts; | ||||||
|  |  | ||||||
| 	std::shared_ptr<CDefFile> getAnimationFile(const AnimationPath & path); | 	std::shared_ptr<CDefFile> getAnimationFile(const AnimationPath & path); | ||||||
| 	std::optional<ResourcePath> getPathForScaleFactor(const ResourcePath & path, const std::string & factor); | 	AnimationLayoutMap & getAnimationLayout(const AnimationPath & path, int scalingFactor, EImageBlitMode mode); | ||||||
| 	std::pair<ResourcePath, int> getScalePath(const ResourcePath & p); | 	void initFromJson(AnimationLayoutMap & layout, const JsonNode & config, EImageBlitMode mode); | ||||||
| 	AnimationLayoutMap & getAnimationLayout(const AnimationPath & path); |  | ||||||
| 	void initFromJson(AnimationLayoutMap & layout, const JsonNode & config); |  | ||||||
|  |  | ||||||
| 	void addImageListEntry(size_t index, size_t group, const std::string & listName, const std::string & imageName); | 	void addImageListEntry(size_t index, size_t group, const std::string & listName, const std::string & imageName); | ||||||
| 	void addImageListEntries(const EntityService * service); | 	void addImageListEntries(const EntityService * service); | ||||||
| 	void storeCachedImage(const ImageLocator & locator, std::shared_ptr<const ISharedImage> image); | 	void storeCachedImage(const ImageLocator & locator, std::shared_ptr<ScalableImageShared> image); | ||||||
|  |  | ||||||
| 	std::shared_ptr<const ISharedImage> loadImageImpl(const ImageLocator & config); | 	std::shared_ptr<ScalableImageShared> loadImageImpl(const ImageLocator & config); | ||||||
|  |  | ||||||
| 	std::shared_ptr<const ISharedImage> loadImageFromFileUncached(const ImageLocator & locator); | 	std::shared_ptr<SDLImageShared> loadImageFromFileUncached(const ImageLocator & locator); | ||||||
| 	std::shared_ptr<const ISharedImage> loadImageFromFile(const ImageLocator & locator); |  | ||||||
|  |  | ||||||
| 	std::shared_ptr<const ISharedImage> transformImage(const ImageLocator & locator, std::shared_ptr<const ISharedImage> image); | 	ImageLocator getLocatorForAnimationFrame(const AnimationPath & path, int frame, int group, EImageBlitMode mode); | ||||||
| 	std::shared_ptr<const ISharedImage> scaleImage(const ImageLocator & locator, std::shared_ptr<const ISharedImage> image); |  | ||||||
|  |  | ||||||
| 	ImageLocator getLocatorForAnimationFrame(const AnimationPath & path, int frame, int group); |  | ||||||
|  |  | ||||||
| 	int getScalingFactor() const; | 	int getScalingFactor() const; | ||||||
|  |  | ||||||
| @@ -55,10 +49,12 @@ public: | |||||||
| 	// IRenderHandler implementation | 	// IRenderHandler implementation | ||||||
| 	void onLibraryLoadingFinished(const Services * services) override; | 	void onLibraryLoadingFinished(const Services * services) override; | ||||||
|  |  | ||||||
| 	std::shared_ptr<IImage> loadImage(const ImageLocator & locator, EImageBlitMode mode) override; | 	std::shared_ptr<IImage> loadImage(const ImageLocator & locator) override; | ||||||
| 	std::shared_ptr<IImage> loadImage(const ImagePath & path, EImageBlitMode mode) override; | 	std::shared_ptr<IImage> loadImage(const ImagePath & path, EImageBlitMode mode) override; | ||||||
| 	std::shared_ptr<IImage> loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode) override; | 	std::shared_ptr<IImage> loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode) override; | ||||||
|  |  | ||||||
|  | 	std::shared_ptr<SDLImageShared> loadSingleImage(const ImageLocator & locator) override; | ||||||
|  |  | ||||||
| 	std::shared_ptr<CAnimation> loadAnimation(const AnimationPath & path, EImageBlitMode mode) override; | 	std::shared_ptr<CAnimation> loadAnimation(const AnimationPath & path, EImageBlitMode mode) override; | ||||||
|  |  | ||||||
| 	std::shared_ptr<IImage> createImage(SDL_Surface * source) override; | 	std::shared_ptr<IImage> createImage(SDL_Surface * source) override; | ||||||
|   | |||||||
| @@ -28,59 +28,6 @@ | |||||||
|  |  | ||||||
| class SDLImageLoader; | class SDLImageLoader; | ||||||
|  |  | ||||||
| //First 8 colors in def palette used for transparency |  | ||||||
| static constexpr std::array<SDL_Color, 8> sourcePalette = {{ |  | ||||||
| 	{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 constexpr std::array<ColorRGBA, 8> targetPalette = {{ |  | ||||||
| 	{0, 0, 0, 0  }, // 0 - transparency                  ( used in most images ) |  | ||||||
| 	{0, 0, 0, 64 }, // 1 - shadow border                 ( used in battle, adventure map def's ) |  | ||||||
| 	{0, 0, 0, 64 }, // 2 - shadow border                 ( used in fog-of-war def's ) |  | ||||||
| 	{0, 0, 0, 128}, // 3 - shadow body                   ( used in fog-of-war def's ) |  | ||||||
| 	{0, 0, 0, 128}, // 4 - shadow body                   ( used in battle, adventure map def's ) |  | ||||||
| 	{0, 0, 0, 0  }, // 5 - selection / owner flag        ( used in battle, adventure map def's ) |  | ||||||
| 	{0, 0, 0, 128}, // 6 - shadow body   below selection ( used in battle def's ) |  | ||||||
| 	{0, 0, 0, 64 }  // 7 - shadow border below selection ( used in battle def's ) |  | ||||||
| }}; |  | ||||||
|  |  | ||||||
| static ui8 mixChannels(ui8 c1, ui8 c2, ui8 a1, ui8 a2) |  | ||||||
| { |  | ||||||
| 	return c1*a1 / 256 + c2*a2*(255 - a1) / 256 / 256; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static ColorRGBA addColors(const ColorRGBA & base, const ColorRGBA & over) |  | ||||||
| { |  | ||||||
| 	return ColorRGBA( |  | ||||||
| 		mixChannels(over.r, base.r, over.a, base.a), |  | ||||||
| 		mixChannels(over.g, base.g, over.a, base.a), |  | ||||||
| 		mixChannels(over.b, base.b, over.a, base.a), |  | ||||||
| 		static_cast<ui8>(over.a + base.a * (255 - over.a) / 256) |  | ||||||
| 		); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static bool colorsSimilar (const SDL_Color & lhs, const SDL_Color & 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 = static_cast<int>(lhs.r) - rhs.r; |  | ||||||
| 	int diffG = static_cast<int>(lhs.g) - rhs.g; |  | ||||||
| 	int diffB = static_cast<int>(lhs.b) - rhs.b; |  | ||||||
| 	int diffA = static_cast<int>(lhs.a) - rhs.a; |  | ||||||
|  |  | ||||||
| 	return std::abs(diffR) < threshold && std::abs(diffG) < threshold && std::abs(diffB) < threshold && std::abs(diffA) < threshold; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int IImage::width() const | int IImage::width() const | ||||||
| { | { | ||||||
| 	return dimensions().x; | 	return dimensions().x; | ||||||
| @@ -91,12 +38,11 @@ int IImage::height() const | |||||||
| 	return dimensions().y; | 	return dimensions().y; | ||||||
| } | } | ||||||
|  |  | ||||||
| SDLImageShared::SDLImageShared(const CDefFile * data, size_t frame, size_t group, int preScaleFactor) | SDLImageShared::SDLImageShared(const CDefFile * data, size_t frame, size_t group) | ||||||
| 	: surf(nullptr), | 	: surf(nullptr), | ||||||
| 	margins(0, 0), | 	margins(0, 0), | ||||||
| 	fullSize(0, 0), | 	fullSize(0, 0), | ||||||
| 	originalPalette(nullptr), | 	originalPalette(nullptr) | ||||||
| 	preScaleFactor(preScaleFactor) |  | ||||||
| { | { | ||||||
| 	SDLImageLoader loader(this); | 	SDLImageLoader loader(this); | ||||||
| 	data->loadFrame(frame, group, loader); | 	data->loadFrame(frame, group, loader); | ||||||
| @@ -104,12 +50,11 @@ SDLImageShared::SDLImageShared(const CDefFile * data, size_t frame, size_t group | |||||||
| 	savePalette(); | 	savePalette(); | ||||||
| } | } | ||||||
|  |  | ||||||
| SDLImageShared::SDLImageShared(SDL_Surface * from, int preScaleFactor) | SDLImageShared::SDLImageShared(SDL_Surface * from) | ||||||
| 	: surf(nullptr), | 	: surf(nullptr), | ||||||
| 	margins(0, 0), | 	margins(0, 0), | ||||||
| 	fullSize(0, 0), | 	fullSize(0, 0), | ||||||
| 	originalPalette(nullptr), | 	originalPalette(nullptr) | ||||||
| 	preScaleFactor(preScaleFactor) |  | ||||||
| { | { | ||||||
| 	surf = from; | 	surf = from; | ||||||
| 	if (surf == nullptr) | 	if (surf == nullptr) | ||||||
| @@ -122,12 +67,11 @@ SDLImageShared::SDLImageShared(SDL_Surface * from, int preScaleFactor) | |||||||
| 	fullSize.y = surf->h; | 	fullSize.y = surf->h; | ||||||
| } | } | ||||||
|  |  | ||||||
| SDLImageShared::SDLImageShared(const ImagePath & filename, int preScaleFactor) | SDLImageShared::SDLImageShared(const ImagePath & filename) | ||||||
| 	: surf(nullptr), | 	: surf(nullptr), | ||||||
| 	margins(0, 0), | 	margins(0, 0), | ||||||
| 	fullSize(0, 0), | 	fullSize(0, 0), | ||||||
| 	originalPalette(nullptr), | 	originalPalette(nullptr) | ||||||
| 	preScaleFactor(preScaleFactor) |  | ||||||
| { | { | ||||||
| 	surf = BitmapHandler::loadBitmap(filename); | 	surf = BitmapHandler::loadBitmap(filename); | ||||||
|  |  | ||||||
| @@ -146,7 +90,6 @@ SDLImageShared::SDLImageShared(const ImagePath & filename, int preScaleFactor) | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| void SDLImageShared::draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const | void SDLImageShared::draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const | ||||||
| { | { | ||||||
| 	if (!surf) | 	if (!surf) | ||||||
| @@ -292,26 +235,20 @@ std::shared_ptr<const ISharedImage> SDLImageShared::scaleInteger(int factor, SDL | |||||||
| 		SDL_SetSurfacePalette(surf, palette); | 		SDL_SetSurfacePalette(surf, palette); | ||||||
|  |  | ||||||
| 	SDL_Surface * scaled = nullptr; | 	SDL_Surface * scaled = nullptr; | ||||||
| 	if(preScaleFactor == factor) |  | ||||||
| 		return shared_from_this(); |  | ||||||
| 	else if(preScaleFactor == 1) |  | ||||||
| 	{ |  | ||||||
| 		// dump heuristics to differentiate tileable UI elements from map object / combat assets |  | ||||||
| 		if (mode == EImageBlitMode::OPAQUE || mode == EImageBlitMode::COLORKEY || mode == EImageBlitMode::SIMPLE) |  | ||||||
| 			scaled = CSDL_Ext::scaleSurfaceIntegerFactor(surf, factor, EScalingAlgorithm::XBRZ_OPAQUE); |  | ||||||
| 		else |  | ||||||
| 			scaled = CSDL_Ext::scaleSurfaceIntegerFactor(surf, factor, EScalingAlgorithm::XBRZ_ALPHA); |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 		scaled = CSDL_Ext::scaleSurface(surf, (int) round((float)surf->w * factor / preScaleFactor), (int) round((float)surf->h * factor / preScaleFactor)); |  | ||||||
|  |  | ||||||
| 	auto ret = std::make_shared<SDLImageShared>(scaled, preScaleFactor); | 	// dump heuristics to differentiate tileable UI elements from map object / combat assets | ||||||
|  | 	if (mode == EImageBlitMode::OPAQUE || mode == EImageBlitMode::COLORKEY || mode == EImageBlitMode::SIMPLE) | ||||||
|  | 		scaled = CSDL_Ext::scaleSurfaceIntegerFactor(surf, factor, EScalingAlgorithm::XBRZ_OPAQUE); | ||||||
|  | 	else | ||||||
|  | 		scaled = CSDL_Ext::scaleSurfaceIntegerFactor(surf, factor, EScalingAlgorithm::XBRZ_ALPHA); | ||||||
|  |  | ||||||
|  | 	auto ret = std::make_shared<SDLImageShared>(scaled); | ||||||
|  |  | ||||||
| 	ret->fullSize.x = fullSize.x * factor; | 	ret->fullSize.x = fullSize.x * factor; | ||||||
| 	ret->fullSize.y = fullSize.y * factor; | 	ret->fullSize.y = fullSize.y * factor; | ||||||
|  |  | ||||||
| 	ret->margins.x = (int) round((float)margins.x * factor / preScaleFactor); | 	ret->margins.x = (int) round((float)margins.x * factor); | ||||||
| 	ret->margins.y = (int) round((float)margins.y * factor / preScaleFactor); | 	ret->margins.y = (int) round((float)margins.y * factor); | ||||||
| 	ret->optimizeSurface(); | 	ret->optimizeSurface(); | ||||||
|  |  | ||||||
| 	// erase our own reference | 	// erase our own reference | ||||||
| @@ -340,7 +277,7 @@ std::shared_ptr<const ISharedImage> SDLImageShared::scaleTo(const Point & size, | |||||||
| 	else | 	else | ||||||
| 		CSDL_Ext::setDefaultColorKey(scaled);//just in case | 		CSDL_Ext::setDefaultColorKey(scaled);//just in case | ||||||
|  |  | ||||||
| 	auto ret = std::make_shared<SDLImageShared>(scaled, preScaleFactor); | 	auto ret = std::make_shared<SDLImageShared>(scaled); | ||||||
|  |  | ||||||
| 	ret->fullSize.x = (int) round((float)fullSize.x * scaleX); | 	ret->fullSize.x = (int) round((float)fullSize.x * scaleX); | ||||||
| 	ret->fullSize.y = (int) round((float)fullSize.y * scaleY); | 	ret->fullSize.y = (int) round((float)fullSize.y * scaleY); | ||||||
| @@ -369,11 +306,6 @@ void SDLImageShared::exportBitmap(const boost::filesystem::path& path, SDL_Palet | |||||||
| 		SDL_SetSurfacePalette(surf, originalPalette); | 		SDL_SetSurfacePalette(surf, originalPalette); | ||||||
| } | } | ||||||
|  |  | ||||||
| void SDLImageIndexed::playerColored(PlayerColor player) |  | ||||||
| { |  | ||||||
| 	graphics->setPlayerPalette(currentPalette, player); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool SDLImageShared::isTransparent(const Point & coords) const | bool SDLImageShared::isTransparent(const Point & coords) const | ||||||
| { | { | ||||||
| 	if (surf) | 	if (surf) | ||||||
| @@ -384,22 +316,21 @@ bool SDLImageShared::isTransparent(const Point & coords) const | |||||||
|  |  | ||||||
| Rect SDLImageShared::contentRect() const | Rect SDLImageShared::contentRect() const | ||||||
| { | { | ||||||
| 	auto tmpMargins = margins / preScaleFactor; | 	auto tmpMargins = margins; | ||||||
| 	auto tmpSize = Point(surf->w, surf->h) / preScaleFactor; | 	auto tmpSize = Point(surf->w, surf->h); | ||||||
| 	return Rect(tmpMargins, tmpSize); | 	return Rect(tmpMargins, tmpSize); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | const SDL_Palette * SDLImageShared::getPalette() const | ||||||
|  | { | ||||||
|  | 	if (!surf) | ||||||
|  | 		return nullptr; | ||||||
|  | 	return surf->format->palette; | ||||||
|  | } | ||||||
|  |  | ||||||
| Point SDLImageShared::dimensions() const | Point SDLImageShared::dimensions() const | ||||||
| { | { | ||||||
| 	return fullSize / preScaleFactor; | 	return fullSize; | ||||||
| } |  | ||||||
|  |  | ||||||
| std::shared_ptr<IImage> SDLImageShared::createImageReference(EImageBlitMode mode) const |  | ||||||
| { |  | ||||||
| 	if (surf && surf->format->palette) |  | ||||||
| 		return std::make_shared<SDLImageIndexed>(shared_from_this(), originalPalette, mode); |  | ||||||
| 	else |  | ||||||
| 		return std::make_shared<SDLImageRGB>(shared_from_this(), mode); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| std::shared_ptr<const ISharedImage> SDLImageShared::horizontalFlip() const | std::shared_ptr<const ISharedImage> SDLImageShared::horizontalFlip() const | ||||||
| @@ -408,7 +339,7 @@ std::shared_ptr<const ISharedImage> SDLImageShared::horizontalFlip() const | |||||||
| 		return shared_from_this(); | 		return shared_from_this(); | ||||||
|  |  | ||||||
| 	SDL_Surface * flipped = CSDL_Ext::horizontalFlip(surf); | 	SDL_Surface * flipped = CSDL_Ext::horizontalFlip(surf); | ||||||
| 	auto ret = std::make_shared<SDLImageShared>(flipped, preScaleFactor); | 	auto ret = std::make_shared<SDLImageShared>(flipped); | ||||||
| 	ret->fullSize = fullSize; | 	ret->fullSize = fullSize; | ||||||
| 	ret->margins.x = margins.x; | 	ret->margins.x = margins.x; | ||||||
| 	ret->margins.y = fullSize.y - surf->h - margins.y; | 	ret->margins.y = fullSize.y - surf->h - margins.y; | ||||||
| @@ -423,7 +354,7 @@ std::shared_ptr<const ISharedImage> SDLImageShared::verticalFlip() const | |||||||
| 		return shared_from_this(); | 		return shared_from_this(); | ||||||
|  |  | ||||||
| 	SDL_Surface * flipped = CSDL_Ext::verticalFlip(surf); | 	SDL_Surface * flipped = CSDL_Ext::verticalFlip(surf); | ||||||
| 	auto ret = std::make_shared<SDLImageShared>(flipped, preScaleFactor); | 	auto ret = std::make_shared<SDLImageShared>(flipped); | ||||||
| 	ret->fullSize = fullSize; | 	ret->fullSize = fullSize; | ||||||
| 	ret->margins.x = fullSize.x - surf->w - margins.x; | 	ret->margins.x = fullSize.x - surf->w - margins.x; | ||||||
| 	ret->margins.y = margins.y; | 	ret->margins.y = margins.y; | ||||||
| @@ -445,219 +376,8 @@ void SDLImageShared::savePalette() | |||||||
| 	SDL_SetPaletteColors(originalPalette, surf->format->palette->colors, 0, surf->format->palette->ncolors); | 	SDL_SetPaletteColors(originalPalette, surf->format->palette->colors, 0, surf->format->palette->ncolors); | ||||||
| } | } | ||||||
|  |  | ||||||
| void SDLImageIndexed::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) |  | ||||||
| { |  | ||||||
| 	std::vector<SDL_Color> shifterColors(colorsToMove); |  | ||||||
|  |  | ||||||
| 	for(uint32_t i=0; i<colorsToMove; ++i) |  | ||||||
| 		shifterColors[(i+distanceToMove)%colorsToMove] = originalPalette->colors[firstColorID + i]; |  | ||||||
|  |  | ||||||
| 	SDL_SetPaletteColors(currentPalette, shifterColors.data(), firstColorID, colorsToMove); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SDLImageIndexed::adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) |  | ||||||
| { |  | ||||||
| 	// If shadow is enabled, following colors must be skipped unconditionally |  | ||||||
| 	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 |  | ||||||
| 	for(int i = 0; i < currentPalette->ncolors; i++) |  | ||||||
| 	{ |  | ||||||
| 		if (i < std::size(sourcePalette) && colorsSimilar(sourcePalette[i], originalPalette->colors[i])) |  | ||||||
| 			continue; |  | ||||||
|  |  | ||||||
| 		if(i < std::numeric_limits<uint32_t>::digits && ((colorsToSkipMask >> i) & 1) == 1) |  | ||||||
| 			continue; |  | ||||||
|  |  | ||||||
| 		currentPalette->colors[i] = CSDL_Ext::toSDL(shifter.shiftColor(CSDL_Ext::fromSDL(originalPalette->colors[i]))); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| SDLImageIndexed::SDLImageIndexed(const std::shared_ptr<const ISharedImage> & image, SDL_Palette * originalPalette, EImageBlitMode mode) |  | ||||||
| 	:SDLImageBase::SDLImageBase(image, mode) |  | ||||||
| 	,originalPalette(originalPalette) |  | ||||||
| { |  | ||||||
| 	currentPalette = SDL_AllocPalette(originalPalette->ncolors); |  | ||||||
| 	SDL_SetPaletteColors(currentPalette, originalPalette->colors, 0, originalPalette->ncolors); |  | ||||||
|  |  | ||||||
| 	preparePalette(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| SDLImageIndexed::~SDLImageIndexed() |  | ||||||
| { |  | ||||||
| 	SDL_FreePalette(currentPalette); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SDLImageIndexed::setShadowTransparency(float factor) |  | ||||||
| { |  | ||||||
| 	ColorRGBA shadow50(0, 0, 0, 128 * factor); |  | ||||||
| 	ColorRGBA shadow25(0, 0, 0,  64 * factor); |  | ||||||
|  |  | ||||||
| 	std::array<SDL_Color, 5> colorsSDL = { |  | ||||||
| 		originalPalette->colors[0], |  | ||||||
| 		originalPalette->colors[1], |  | ||||||
| 		originalPalette->colors[2], |  | ||||||
| 		originalPalette->colors[3], |  | ||||||
| 		originalPalette->colors[4] |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	// seems to be used unconditionally |  | ||||||
| 	colorsSDL[0] = CSDL_Ext::toSDL(Colors::TRANSPARENCY); |  | ||||||
| 	colorsSDL[1] = CSDL_Ext::toSDL(shadow25); |  | ||||||
| 	colorsSDL[4] = CSDL_Ext::toSDL(shadow50); |  | ||||||
|  |  | ||||||
| 	// seems to be used only if color matches |  | ||||||
| 	if (colorsSimilar(originalPalette->colors[2], sourcePalette[2])) |  | ||||||
| 		colorsSDL[2] = CSDL_Ext::toSDL(shadow25); |  | ||||||
|  |  | ||||||
| 	if (colorsSimilar(originalPalette->colors[3], sourcePalette[3])) |  | ||||||
| 		colorsSDL[3] = CSDL_Ext::toSDL(shadow50); |  | ||||||
|  |  | ||||||
| 	SDL_SetPaletteColors(currentPalette, colorsSDL.data(), 0, colorsSDL.size()); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SDLImageIndexed::setOverlayColor(const ColorRGBA & color) |  | ||||||
| { |  | ||||||
| 	currentPalette->colors[5] = CSDL_Ext::toSDL(addColors(targetPalette[5], color)); |  | ||||||
|  |  | ||||||
| 	for (int i : {6,7}) |  | ||||||
| 	{ |  | ||||||
| 		if (colorsSimilar(originalPalette->colors[i], sourcePalette[i])) |  | ||||||
| 			currentPalette->colors[i] = CSDL_Ext::toSDL(addColors(targetPalette[i], color)); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SDLImageIndexed::preparePalette() |  | ||||||
| { |  | ||||||
| 	switch(blitMode) |  | ||||||
| 	{ |  | ||||||
| 		case EImageBlitMode::ONLY_SHADOW: |  | ||||||
| 		case EImageBlitMode::ONLY_OVERLAY: |  | ||||||
| 			adjustPalette(ColorFilter::genAlphaShifter(0), 0); |  | ||||||
| 			break; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	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; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	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() | SDLImageShared::~SDLImageShared() | ||||||
| { | { | ||||||
| 	SDL_FreeSurface(surf); | 	SDL_FreeSurface(surf); | ||||||
| 	SDL_FreePalette(originalPalette); | 	SDL_FreePalette(originalPalette); | ||||||
| } | } | ||||||
|  |  | ||||||
| SDLImageBase::SDLImageBase(const std::shared_ptr<const ISharedImage> & image, EImageBlitMode mode) |  | ||||||
| 	:image(image) |  | ||||||
| 	, alphaValue(SDL_ALPHA_OPAQUE) |  | ||||||
| 	, blitMode(mode) |  | ||||||
| {} |  | ||||||
|  |  | ||||||
| std::shared_ptr<const ISharedImage> SDLImageBase::getSharedImage() const |  | ||||||
| { |  | ||||||
| 	return image; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SDLImageRGB::draw(SDL_Surface * where, const Point & pos, const Rect * src) const |  | ||||||
| { |  | ||||||
| 	image->draw(where, nullptr, pos, src, Colors::WHITE_TRUE, alphaValue, blitMode); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SDLImageIndexed::draw(SDL_Surface * where, const Point & pos, const Rect * src) const |  | ||||||
| { |  | ||||||
| 	image->draw(where, currentPalette, pos, src, Colors::WHITE_TRUE, alphaValue, blitMode); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SDLImageIndexed::exportBitmap(const boost::filesystem::path & path) const |  | ||||||
| { |  | ||||||
| 	image->exportBitmap(path, currentPalette); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SDLImageIndexed::scaleTo(const Point & size) |  | ||||||
| { |  | ||||||
| 	image = image->scaleTo(size, currentPalette); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SDLImageRGB::scaleTo(const Point & size) |  | ||||||
| { |  | ||||||
| 	image = image->scaleTo(size, nullptr); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SDLImageIndexed::scaleInteger(int factor) |  | ||||||
| { |  | ||||||
| 	image = image->scaleInteger(factor, currentPalette, blitMode); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SDLImageRGB::scaleInteger(int factor) |  | ||||||
| { |  | ||||||
| 	image = image->scaleInteger(factor, nullptr, blitMode); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SDLImageRGB::exportBitmap(const boost::filesystem::path & path) const |  | ||||||
| { |  | ||||||
| 	image->exportBitmap(path, nullptr); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool SDLImageBase::isTransparent(const Point & coords) const |  | ||||||
| { |  | ||||||
| 	return image->isTransparent(coords); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Rect SDLImageBase::contentRect() const |  | ||||||
| { |  | ||||||
| 	return image->contentRect(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Point SDLImageBase::dimensions() const |  | ||||||
| { |  | ||||||
| 	return image->dimensions(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SDLImageBase::setAlpha(uint8_t value) |  | ||||||
| { |  | ||||||
| 	alphaValue = value; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SDLImageBase::setBlitMode(EImageBlitMode mode) |  | ||||||
| { |  | ||||||
| 	blitMode = mode; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SDLImageRGB::setOverlayColor(const ColorRGBA & color) |  | ||||||
| {} |  | ||||||
|  |  | ||||||
| void SDLImageRGB::playerColored(PlayerColor player) |  | ||||||
| {} |  | ||||||
|  |  | ||||||
| void SDLImageRGB::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) |  | ||||||
| {} |  | ||||||
|  |  | ||||||
| void SDLImageRGB::adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) |  | ||||||
| {} |  | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -35,9 +35,6 @@ class SDLImageShared final : public ISharedImage, public std::enable_shared_from | |||||||
| 	//total size including borders | 	//total size including borders | ||||||
| 	Point fullSize; | 	Point fullSize; | ||||||
|  |  | ||||||
| 	//pre scaled image |  | ||||||
| 	int preScaleFactor; |  | ||||||
|  |  | ||||||
| 	// Keep the original palette, in order to do color switching operation | 	// Keep the original palette, in order to do color switching operation | ||||||
| 	void savePalette(); | 	void savePalette(); | ||||||
|  |  | ||||||
| @@ -45,11 +42,11 @@ class SDLImageShared final : public ISharedImage, public std::enable_shared_from | |||||||
|  |  | ||||||
| public: | public: | ||||||
| 	//Load image from def file | 	//Load image from def file | ||||||
| 	SDLImageShared(const CDefFile *data, size_t frame, size_t group=0, int preScaleFactor=1); | 	SDLImageShared(const CDefFile *data, size_t frame, size_t group=0); | ||||||
| 	//Load from bitmap file | 	//Load from bitmap file | ||||||
| 	SDLImageShared(const ImagePath & filename, int preScaleFactor=1); | 	SDLImageShared(const ImagePath & filename); | ||||||
| 	//Create using existing surface, extraRef will increase refcount on SDL_Surface | 	//Create using existing surface, extraRef will increase refcount on SDL_Surface | ||||||
| 	SDLImageShared(SDL_Surface * from, int preScaleFactor=1); | 	SDLImageShared(SDL_Surface * from); | ||||||
| 	~SDLImageShared(); | 	~SDLImageShared(); | ||||||
|  |  | ||||||
| 	void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const override; | 	void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const override; | ||||||
| @@ -58,7 +55,9 @@ public: | |||||||
| 	Point dimensions() const override; | 	Point dimensions() const override; | ||||||
| 	bool isTransparent(const Point & coords) const override; | 	bool isTransparent(const Point & coords) const override; | ||||||
| 	Rect contentRect() const override; | 	Rect contentRect() const override; | ||||||
| 	[[nodiscard]] std::shared_ptr<IImage> createImageReference(EImageBlitMode mode) const override; |  | ||||||
|  | 	const SDL_Palette * getPalette() const override; | ||||||
|  |  | ||||||
| 	[[nodiscard]] std::shared_ptr<const ISharedImage> horizontalFlip() const override; | 	[[nodiscard]] std::shared_ptr<const ISharedImage> horizontalFlip() const override; | ||||||
| 	[[nodiscard]] std::shared_ptr<const ISharedImage> verticalFlip() const override; | 	[[nodiscard]] std::shared_ptr<const ISharedImage> verticalFlip() const override; | ||||||
| 	[[nodiscard]] std::shared_ptr<const ISharedImage> scaleInteger(int factor, SDL_Palette * palette, EImageBlitMode blitMode) const override; | 	[[nodiscard]] std::shared_ptr<const ISharedImage> scaleInteger(int factor, SDL_Palette * palette, EImageBlitMode blitMode) const override; | ||||||
| @@ -66,58 +65,3 @@ public: | |||||||
|  |  | ||||||
| 	friend class SDLImageLoader; | 	friend class SDLImageLoader; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class SDLImageBase : public IImage, boost::noncopyable |  | ||||||
| { |  | ||||||
| protected: |  | ||||||
| 	std::shared_ptr<const ISharedImage> image; |  | ||||||
|  |  | ||||||
| 	uint8_t alphaValue; |  | ||||||
| 	EImageBlitMode blitMode; |  | ||||||
|  |  | ||||||
| public: |  | ||||||
| 	SDLImageBase(const std::shared_ptr<const ISharedImage> & image, EImageBlitMode mode); |  | ||||||
|  |  | ||||||
| 	bool isTransparent(const Point & coords) const override; |  | ||||||
| 	Rect contentRect() const override; |  | ||||||
| 	Point dimensions() const override; |  | ||||||
| 	void setAlpha(uint8_t value) override; |  | ||||||
| 	void setBlitMode(EImageBlitMode mode) override; |  | ||||||
| 	std::shared_ptr<const ISharedImage> getSharedImage() const override; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| class SDLImageIndexed final : public SDLImageBase |  | ||||||
| { |  | ||||||
| 	SDL_Palette * currentPalette = nullptr; |  | ||||||
| 	SDL_Palette * originalPalette = nullptr; |  | ||||||
|  |  | ||||||
| 	void setShadowTransparency(float factor); |  | ||||||
| 	void preparePalette(); |  | ||||||
| public: |  | ||||||
| 	SDLImageIndexed(const std::shared_ptr<const ISharedImage> & image, SDL_Palette * palette, EImageBlitMode mode); |  | ||||||
| 	~SDLImageIndexed(); |  | ||||||
|  |  | ||||||
| 	void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override; |  | ||||||
| 	void setOverlayColor(const ColorRGBA & color) override; |  | ||||||
| 	void playerColored(PlayerColor player) override; |  | ||||||
| 	void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; |  | ||||||
| 	void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; |  | ||||||
| 	void scaleInteger(int factor) override; |  | ||||||
| 	void scaleTo(const Point & size) override; |  | ||||||
| 	void exportBitmap(const boost::filesystem::path & path) const override; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| class SDLImageRGB final : public SDLImageBase |  | ||||||
| { |  | ||||||
| public: |  | ||||||
| 	using SDLImageBase::SDLImageBase; |  | ||||||
|  |  | ||||||
| 	void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override; |  | ||||||
| 	void setOverlayColor(const ColorRGBA & color) override; |  | ||||||
| 	void playerColored(PlayerColor player) override; |  | ||||||
| 	void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; |  | ||||||
| 	void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; |  | ||||||
| 	void scaleInteger(int factor) override; |  | ||||||
| 	void scaleTo(const Point & size) override; |  | ||||||
| 	void exportBitmap(const boost::filesystem::path & path) const override; |  | ||||||
| }; |  | ||||||
|   | |||||||
							
								
								
									
										489
									
								
								client/renderSDL/ScalableImage.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										489
									
								
								client/renderSDL/ScalableImage.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,489 @@ | |||||||
|  | /* | ||||||
|  |  * ScalableImage.cpp, part of VCMI engine | ||||||
|  |  * | ||||||
|  |  * Authors: listed in file AUTHORS in main folder | ||||||
|  |  * | ||||||
|  |  * License: GNU General Public License v2.0 or later | ||||||
|  |  * Full text of license available in license.txt file, in main folder | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | #include "StdInc.h" | ||||||
|  | #include "ScalableImage.h" | ||||||
|  |  | ||||||
|  | #include "SDLImage.h" | ||||||
|  | #include "SDL_Extensions.h" | ||||||
|  |  | ||||||
|  | #include "../gui/CGuiHandler.h" | ||||||
|  |  | ||||||
|  | #include "../render/ColorFilter.h" | ||||||
|  | #include "../render/Colors.h" | ||||||
|  | #include "../render/Graphics.h" | ||||||
|  | #include "../render/IRenderHandler.h" | ||||||
|  | #include "../render/IScreenHandler.h" | ||||||
|  |  | ||||||
|  | #include "../../lib/constants/EntityIdentifiers.h" | ||||||
|  |  | ||||||
|  | #include <SDL_surface.h> | ||||||
|  |  | ||||||
|  | //First 8 colors in def palette used for transparency | ||||||
|  | static constexpr std::array<SDL_Color, 8> sourcePalette = {{ | ||||||
|  | 	{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 constexpr std::array<ColorRGBA, 8> targetPalette = {{ | ||||||
|  | 	{0, 0, 0, 0  }, // 0 - transparency                  ( used in most images ) | ||||||
|  | 	{0, 0, 0, 64 }, // 1 - shadow border                 ( used in battle, adventure map def's ) | ||||||
|  | 	{0, 0, 0, 64 }, // 2 - shadow border                 ( used in fog-of-war def's ) | ||||||
|  | 	{0, 0, 0, 128}, // 3 - shadow body                   ( used in fog-of-war def's ) | ||||||
|  | 	{0, 0, 0, 128}, // 4 - shadow body                   ( used in battle, adventure map def's ) | ||||||
|  | 	{0, 0, 0, 0  }, // 5 - selection / owner flag        ( used in battle, adventure map def's ) | ||||||
|  | 	{0, 0, 0, 128}, // 6 - shadow body   below selection ( used in battle def's ) | ||||||
|  | 	{0, 0, 0, 64 }  // 7 - shadow border below selection ( used in battle def's ) | ||||||
|  | }}; | ||||||
|  |  | ||||||
|  | static ui8 mixChannels(ui8 c1, ui8 c2, ui8 a1, ui8 a2) | ||||||
|  | { | ||||||
|  | 	return c1*a1 / 256 + c2*a2*(255 - a1) / 256 / 256; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static ColorRGBA addColors(const ColorRGBA & base, const ColorRGBA & over) | ||||||
|  | { | ||||||
|  | 	return ColorRGBA( | ||||||
|  | 		mixChannels(over.r, base.r, over.a, base.a), | ||||||
|  | 		mixChannels(over.g, base.g, over.a, base.a), | ||||||
|  | 		mixChannels(over.b, base.b, over.a, base.a), | ||||||
|  | 		static_cast<ui8>(over.a + base.a * (255 - over.a) / 256) | ||||||
|  | 		); | ||||||
|  | } | ||||||
|  | static bool colorsSimilar (const SDL_Color & lhs, const SDL_Color & 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 = static_cast<int>(lhs.r) - rhs.r; | ||||||
|  | 	int diffG = static_cast<int>(lhs.g) - rhs.g; | ||||||
|  | 	int diffB = static_cast<int>(lhs.b) - rhs.b; | ||||||
|  | 	int diffA = static_cast<int>(lhs.a) - rhs.a; | ||||||
|  |  | ||||||
|  | 	return std::abs(diffR) < threshold && std::abs(diffG) < threshold && std::abs(diffB) < threshold && std::abs(diffA) < threshold; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ScalableImageParameters::ScalableImageParameters(const SDL_Palette * originalPalette, EImageBlitMode blitMode) | ||||||
|  | { | ||||||
|  | 	if (originalPalette) | ||||||
|  | 	{ | ||||||
|  | 		palette = SDL_AllocPalette(originalPalette->ncolors); | ||||||
|  | 		SDL_SetPaletteColors(palette, originalPalette->colors, 0, originalPalette->ncolors); | ||||||
|  | 		preparePalette(originalPalette, blitMode); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ScalableImageParameters::~ScalableImageParameters() | ||||||
|  | { | ||||||
|  | 	SDL_FreePalette(palette); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScalableImageParameters::preparePalette(const SDL_Palette * originalPalette, EImageBlitMode blitMode) | ||||||
|  | { | ||||||
|  | 	switch(blitMode) | ||||||
|  | 	{ | ||||||
|  | 		case EImageBlitMode::ONLY_SHADOW: | ||||||
|  | 		case EImageBlitMode::ONLY_OVERLAY: | ||||||
|  | 			adjustPalette(originalPalette, blitMode, ColorFilter::genAlphaShifter(0), 0); | ||||||
|  | 			break; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch(blitMode) | ||||||
|  | 	{ | ||||||
|  | 		case EImageBlitMode::SIMPLE: | ||||||
|  | 		case EImageBlitMode::WITH_SHADOW: | ||||||
|  | 		case EImageBlitMode::ONLY_SHADOW: | ||||||
|  | 		case EImageBlitMode::WITH_SHADOW_AND_OVERLAY: | ||||||
|  | 			setShadowTransparency(originalPalette, 1.0); | ||||||
|  | 			break; | ||||||
|  | 		case EImageBlitMode::ONLY_BODY: | ||||||
|  | 		case EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY: | ||||||
|  | 		case EImageBlitMode::ONLY_OVERLAY: | ||||||
|  | 			setShadowTransparency(originalPalette, 0.0); | ||||||
|  | 			break; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch(blitMode) | ||||||
|  | 	{ | ||||||
|  | 		case EImageBlitMode::ONLY_OVERLAY: | ||||||
|  | 		case EImageBlitMode::WITH_SHADOW_AND_OVERLAY: | ||||||
|  | 			setOverlayColor(originalPalette, Colors::WHITE_TRUE); | ||||||
|  | 			break; | ||||||
|  | 		case EImageBlitMode::ONLY_SHADOW: | ||||||
|  | 		case EImageBlitMode::ONLY_BODY: | ||||||
|  | 			setOverlayColor(originalPalette, Colors::TRANSPARENCY); | ||||||
|  | 			break; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScalableImageParameters::setOverlayColor(const SDL_Palette * originalPalette, const ColorRGBA & color) | ||||||
|  | { | ||||||
|  | 	palette->colors[5] = CSDL_Ext::toSDL(addColors(targetPalette[5], color)); | ||||||
|  |  | ||||||
|  | 	for (int i : {6,7}) | ||||||
|  | 	{ | ||||||
|  | 		if (colorsSimilar(originalPalette->colors[i], sourcePalette[i])) | ||||||
|  | 			palette->colors[i] = CSDL_Ext::toSDL(addColors(targetPalette[i], color)); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScalableImageParameters::shiftPalette(const SDL_Palette * originalPalette, uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) | ||||||
|  | { | ||||||
|  | 	std::vector<SDL_Color> shifterColors(colorsToMove); | ||||||
|  |  | ||||||
|  | 	for(uint32_t i=0; i<colorsToMove; ++i) | ||||||
|  | 		shifterColors[(i+distanceToMove)%colorsToMove] = originalPalette->colors[firstColorID + i]; | ||||||
|  |  | ||||||
|  | 	SDL_SetPaletteColors(palette, shifterColors.data(), firstColorID, colorsToMove); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScalableImageParameters::setShadowTransparency(const SDL_Palette * originalPalette, float factor) | ||||||
|  | { | ||||||
|  | 	ColorRGBA shadow50(0, 0, 0, 128 * factor); | ||||||
|  | 	ColorRGBA shadow25(0, 0, 0,  64 * factor); | ||||||
|  |  | ||||||
|  | 	std::array<SDL_Color, 5> colorsSDL = { | ||||||
|  | 		originalPalette->colors[0], | ||||||
|  | 		originalPalette->colors[1], | ||||||
|  | 		originalPalette->colors[2], | ||||||
|  | 		originalPalette->colors[3], | ||||||
|  | 		originalPalette->colors[4] | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	// seems to be used unconditionally | ||||||
|  | 	colorsSDL[0] = CSDL_Ext::toSDL(Colors::TRANSPARENCY); | ||||||
|  | 	colorsSDL[1] = CSDL_Ext::toSDL(shadow25); | ||||||
|  | 	colorsSDL[4] = CSDL_Ext::toSDL(shadow50); | ||||||
|  |  | ||||||
|  | 	// seems to be used only if color matches | ||||||
|  | 	if (colorsSimilar(originalPalette->colors[2], sourcePalette[2])) | ||||||
|  | 		colorsSDL[2] = CSDL_Ext::toSDL(shadow25); | ||||||
|  |  | ||||||
|  | 	if (colorsSimilar(originalPalette->colors[3], sourcePalette[3])) | ||||||
|  | 		colorsSDL[3] = CSDL_Ext::toSDL(shadow50); | ||||||
|  |  | ||||||
|  | 	SDL_SetPaletteColors(palette, colorsSDL.data(), 0, colorsSDL.size()); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScalableImageParameters::adjustPalette(const SDL_Palette * originalPalette, EImageBlitMode blitMode, const ColorFilter & shifter, uint32_t colorsToSkipMask) | ||||||
|  | { | ||||||
|  | 	// If shadow is enabled, following colors must be skipped unconditionally | ||||||
|  | 	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 | ||||||
|  | 	for(int i = 0; i < palette->ncolors; i++) | ||||||
|  | 	{ | ||||||
|  | 		if (i < std::size(sourcePalette) && colorsSimilar(sourcePalette[i], originalPalette->colors[i])) | ||||||
|  | 			continue; | ||||||
|  |  | ||||||
|  | 		if(i < std::numeric_limits<uint32_t>::digits && ((colorsToSkipMask >> i) & 1) == 1) | ||||||
|  | 			continue; | ||||||
|  |  | ||||||
|  | 		palette->colors[i] = CSDL_Ext::toSDL(shifter.shiftColor(CSDL_Ext::fromSDL(originalPalette->colors[i]))); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ScalableImageShared::ScalableImageShared(const SharedImageLocator & locator, const std::shared_ptr<const ISharedImage> & baseImage) | ||||||
|  | 	:locator(locator) | ||||||
|  | { | ||||||
|  | 	base[0] = baseImage; | ||||||
|  | 	assert(base[0] != nullptr); | ||||||
|  |  | ||||||
|  | 	loadScaledImages(GH.screenHandler().getScalingFactor(), PlayerColor::CANNOT_DETERMINE); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Point ScalableImageShared::dimensions() const | ||||||
|  | { | ||||||
|  | 	return base[0]->dimensions(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScalableImageShared::exportBitmap(const boost::filesystem::path & path, const ScalableImageParameters & parameters) const | ||||||
|  | { | ||||||
|  | 	base[0]->exportBitmap(path, parameters.palette); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool ScalableImageShared::isTransparent(const Point & coords) const | ||||||
|  | { | ||||||
|  | 	return base[0]->isTransparent(coords); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Rect ScalableImageShared::contentRect() const | ||||||
|  | { | ||||||
|  | 	return base[0]->contentRect(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScalableImageShared::draw(SDL_Surface * where, const Point & dest, const Rect * src, const ScalableImageParameters & parameters, int scalingFactor) | ||||||
|  | { | ||||||
|  | 	const auto & flipAndDraw = [&](FlippedImages & images, const ColorRGBA & colorMultiplier, uint8_t alphaValue){ | ||||||
|  | 		int index = 0; | ||||||
|  | 		if (parameters.flipVertical) | ||||||
|  | 		{ | ||||||
|  | 			if (!images[index|1]) | ||||||
|  | 				images[index|1] = images[index]->verticalFlip(); | ||||||
|  |  | ||||||
|  | 			index |= 1; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if (parameters.flipHorizontal) | ||||||
|  | 		{ | ||||||
|  | 			if (!images[index|2]) | ||||||
|  | 				images[index|2] = images[index]->horizontalFlip(); | ||||||
|  |  | ||||||
|  | 			index |= 2; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		images[index]->draw(where, parameters.palette, dest, src, colorMultiplier, alphaValue, locator.layer); | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	if (scalingFactor == 1) | ||||||
|  | 	{ | ||||||
|  | 		flipAndDraw(base, parameters.colorMultiplier, parameters.alphaValue); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		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[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); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const SDL_Palette * ScalableImageShared::getPalette() const | ||||||
|  | { | ||||||
|  | 	return base[0]->getPalette(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::shared_ptr<ScalableImageInstance> ScalableImageShared::createImageReference() | ||||||
|  | { | ||||||
|  | 	return std::make_shared<ScalableImageInstance>(shared_from_this(), locator.layer); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ScalableImageInstance::ScalableImageInstance(const std::shared_ptr<ScalableImageShared> & image, EImageBlitMode blitMode) | ||||||
|  | 	:image(image) | ||||||
|  | 	,parameters(image->getPalette(), blitMode) | ||||||
|  | 	,blitMode(blitMode) | ||||||
|  | { | ||||||
|  | 	assert(image); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScalableImageInstance::scaleTo(const Point & size) | ||||||
|  | { | ||||||
|  | 	assert(0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScalableImageInstance::exportBitmap(const boost::filesystem::path & path) const | ||||||
|  | { | ||||||
|  | 	image->exportBitmap(path, parameters); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool ScalableImageInstance::isTransparent(const Point & coords) const | ||||||
|  | { | ||||||
|  | 	return image->isTransparent(coords); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Rect ScalableImageInstance::contentRect() const | ||||||
|  | { | ||||||
|  | 	return image->contentRect(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Point ScalableImageInstance::dimensions() const | ||||||
|  | { | ||||||
|  | 	return image->dimensions(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScalableImageInstance::setAlpha(uint8_t value) | ||||||
|  | { | ||||||
|  | 	parameters.alphaValue = value; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScalableImageInstance::draw(SDL_Surface * where, const Point & pos, const Rect * src, int scalingFactor) const | ||||||
|  | { | ||||||
|  | 	image->draw(where, pos, src, parameters, scalingFactor); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScalableImageInstance::setOverlayColor(const ColorRGBA & color) | ||||||
|  | { | ||||||
|  | 	parameters.ovelayColorMultiplier = color; | ||||||
|  |  | ||||||
|  | 	if (parameters.palette) | ||||||
|  | 		parameters.setOverlayColor(image->getPalette(), color); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScalableImageInstance::playerColored(PlayerColor player) | ||||||
|  | { | ||||||
|  | 	parameters.player = player; | ||||||
|  |  | ||||||
|  | 	if (!parameters.palette) | ||||||
|  | 		parameters.playerColored(player); | ||||||
|  |  | ||||||
|  | 	image->preparePlayerColoredImage(player); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScalableImageParameters::playerColored(PlayerColor player) | ||||||
|  | { | ||||||
|  | 	graphics->setPlayerPalette(palette, player); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScalableImageInstance::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) | ||||||
|  | { | ||||||
|  | 	if (parameters.palette) | ||||||
|  | 		parameters.shiftPalette(image->getPalette(),firstColorID, colorsToMove, distanceToMove); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScalableImageInstance::adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) | ||||||
|  | { | ||||||
|  | 	if (parameters.palette) | ||||||
|  | 		parameters.adjustPalette(image->getPalette(), blitMode, shifter, colorsToSkipMask); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScalableImageInstance::horizontalFlip() | ||||||
|  | { | ||||||
|  | 	parameters.flipHorizontal = !parameters.flipHorizontal; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScalableImageInstance::verticalFlip() | ||||||
|  | { | ||||||
|  | 	parameters.flipVertical = !parameters.flipVertical; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::shared_ptr<const ISharedImage> ScalableImageShared::loadOrGenerateImage(EImageBlitMode mode, int8_t scalingFactor, PlayerColor color) const | ||||||
|  | { | ||||||
|  | 	ImageLocator loadingLocator; | ||||||
|  |  | ||||||
|  | 	loadingLocator.image = locator.image; | ||||||
|  | 	loadingLocator.defFile = locator.defFile; | ||||||
|  | 	loadingLocator.defFrame = locator.defFrame; | ||||||
|  | 	loadingLocator.defGroup = locator.defGroup; | ||||||
|  | 	loadingLocator.layer = mode; | ||||||
|  | 	loadingLocator.scalingFactor = scalingFactor; | ||||||
|  | 	loadingLocator.playerColored = color; | ||||||
|  |  | ||||||
|  | 	// best case - requested image is already available in filesystem | ||||||
|  | 	auto loadedImage = GH.renderHandler().loadSingleImage(loadingLocator); | ||||||
|  | 	if (loadedImage) | ||||||
|  | 		return loadedImage; | ||||||
|  |  | ||||||
|  | 	// 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) | ||||||
|  | 	{ | ||||||
|  | 		loadingLocator.scalingFactor = scaling; | ||||||
|  | 		auto loadedImage = GH.renderHandler().loadSingleImage(loadingLocator); | ||||||
|  | 		if (loadedImage) | ||||||
|  | 			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 (color != PlayerColor::CANNOT_DETERMINE) | ||||||
|  | 		parameters.playerColored(color); | ||||||
|  | 	return base[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) | ||||||
|  | 	{ | ||||||
|  | 		switch(locator.layer) | ||||||
|  | 		{ | ||||||
|  | 			case EImageBlitMode::OPAQUE: | ||||||
|  | 			case EImageBlitMode::COLORKEY: | ||||||
|  | 			case EImageBlitMode::SIMPLE: | ||||||
|  | 				scaled[scalingFactor].body[0] = loadOrGenerateImage(locator.layer, scalingFactor, PlayerColor::CANNOT_DETERMINE); | ||||||
|  | 				break; | ||||||
|  |  | ||||||
|  | 			case EImageBlitMode::WITH_SHADOW_AND_OVERLAY: | ||||||
|  | 			case EImageBlitMode::ONLY_BODY: | ||||||
|  | 				scaled[scalingFactor].body[0] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY, scalingFactor, PlayerColor::CANNOT_DETERMINE); | ||||||
|  | 				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); | ||||||
|  | 				break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (color != PlayerColor::CANNOT_DETERMINE && scaled[scalingFactor].playerColored[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); | ||||||
|  | 				break; | ||||||
|  |  | ||||||
|  | 			case EImageBlitMode::WITH_SHADOW_AND_OVERLAY: | ||||||
|  | 			case EImageBlitMode::ONLY_BODY: | ||||||
|  | 				scaled[scalingFactor].playerColored[color.getNum()] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY, scalingFactor, color); | ||||||
|  | 				break; | ||||||
|  |  | ||||||
|  | 			case EImageBlitMode::WITH_SHADOW: | ||||||
|  | 			case EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY: | ||||||
|  | 				scaled[scalingFactor].playerColored[color.getNum()] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY, scalingFactor, color); | ||||||
|  | 				break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (scaled[scalingFactor].shadow[0] == nullptr) | ||||||
|  | 	{ | ||||||
|  | 		switch(locator.layer) | ||||||
|  | 		{ | ||||||
|  | 			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); | ||||||
|  | 				break; | ||||||
|  | 			default: | ||||||
|  | 				break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (scaled[scalingFactor].overlay[0] == nullptr) | ||||||
|  | 	{ | ||||||
|  | 		switch(locator.layer) | ||||||
|  | 		{ | ||||||
|  | 			case EImageBlitMode::ONLY_OVERLAY: | ||||||
|  | 			case EImageBlitMode::WITH_SHADOW_AND_OVERLAY: | ||||||
|  | 				scaled[scalingFactor].overlay[0] = loadOrGenerateImage(EImageBlitMode::ONLY_OVERLAY, scalingFactor, PlayerColor::CANNOT_DETERMINE); | ||||||
|  | 				break; | ||||||
|  | 			default: | ||||||
|  | 				break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ScalableImageShared::preparePlayerColoredImage(PlayerColor color) | ||||||
|  | { | ||||||
|  | 	loadScaledImages(GH.screenHandler().getScalingFactor(), color); | ||||||
|  | } | ||||||
							
								
								
									
										124
									
								
								client/renderSDL/ScalableImage.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								client/renderSDL/ScalableImage.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | |||||||
|  | /* | ||||||
|  |  * ScalableImage.h, part of VCMI engine | ||||||
|  |  * | ||||||
|  |  * Authors: listed in file AUTHORS in main folder | ||||||
|  |  * | ||||||
|  |  * License: GNU General Public License v2.0 or later | ||||||
|  |  * Full text of license available in license.txt file, in main folder | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "../render/IImage.h" | ||||||
|  | #include "../render/ImageLocator.h" | ||||||
|  | #include "../render/Colors.h" | ||||||
|  |  | ||||||
|  | #include "../../lib/Color.h" | ||||||
|  |  | ||||||
|  | struct SDL_Palette; | ||||||
|  |  | ||||||
|  | class ScalableImageInstance; | ||||||
|  |  | ||||||
|  | struct ScalableImageParameters : boost::noncopyable | ||||||
|  | { | ||||||
|  | 	SDL_Palette * palette = nullptr; | ||||||
|  |  | ||||||
|  | 	ColorRGBA colorMultiplier = Colors::WHITE_TRUE; | ||||||
|  | 	ColorRGBA ovelayColorMultiplier = Colors::WHITE_TRUE; | ||||||
|  |  | ||||||
|  | 	PlayerColor player = PlayerColor::CANNOT_DETERMINE; | ||||||
|  | 	uint8_t alphaValue = 255; | ||||||
|  |  | ||||||
|  | 	bool flipVertical = false; | ||||||
|  | 	bool flipHorizontal = false; | ||||||
|  |  | ||||||
|  | 	ScalableImageParameters(const SDL_Palette * originalPalette, EImageBlitMode blitMode); | ||||||
|  | 	~ScalableImageParameters(); | ||||||
|  |  | ||||||
|  | 	void setShadowTransparency(const SDL_Palette * originalPalette, float factor); | ||||||
|  | 	void shiftPalette(const SDL_Palette * originalPalette, uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove); | ||||||
|  | 	void playerColored(PlayerColor player); | ||||||
|  | 	void setOverlayColor(const SDL_Palette * originalPalette, const ColorRGBA & color); | ||||||
|  | 	void preparePalette(const SDL_Palette * originalPalette, EImageBlitMode blitMode); | ||||||
|  | 	void adjustPalette(const SDL_Palette * originalPalette, EImageBlitMode blitMode, const ColorFilter & shifter, uint32_t colorsToSkipMask); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class ScalableImageShared final : public std::enable_shared_from_this<ScalableImageShared>, boost::noncopyable | ||||||
|  | { | ||||||
|  | 	static constexpr int maxScaling = 4; | ||||||
|  | 	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>; | ||||||
|  |  | ||||||
|  | 	struct ScaledImage | ||||||
|  | 	{ | ||||||
|  | 		/// Upscaled shadow of our image, may be null | ||||||
|  | 		FlippedImages shadow; | ||||||
|  |  | ||||||
|  | 		/// Upscaled main part of our image, may be null | ||||||
|  | 		FlippedImages body; | ||||||
|  |  | ||||||
|  | 		/// Upscaled overlay (player color, selection highlight) of our image, may be null | ||||||
|  | 		FlippedImages overlay; | ||||||
|  |  | ||||||
|  | 		// player-colored images of this particular scale. These are never flipped in h3 | ||||||
|  | 		PlayerColoredImages playerColored; | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	// 1x image, usually indexed. Array of 4 images for every potential flip | ||||||
|  | 	FlippedImages base; | ||||||
|  |  | ||||||
|  | 	// 1x-4x images. 1x versions are only used if dedicated image is present, othervice palette transform is applied to base image | ||||||
|  | 	std::array<ScaledImage, maxScaling> scaled; | ||||||
|  |  | ||||||
|  | 	const SharedImageLocator locator; | ||||||
|  |  | ||||||
|  | 	std::shared_ptr<const ISharedImage> loadOrGenerateImage(EImageBlitMode mode, int8_t scalingFactor, PlayerColor color) const; | ||||||
|  |  | ||||||
|  | 	void loadScaledImages(int8_t scalingFactor, PlayerColor color); | ||||||
|  |  | ||||||
|  | public: | ||||||
|  | 	ScalableImageShared(const SharedImageLocator & locator, const std::shared_ptr<const ISharedImage> & baseImage); | ||||||
|  |  | ||||||
|  | 	Point dimensions() const; | ||||||
|  | 	void exportBitmap(const boost::filesystem::path & path, const ScalableImageParameters & parameters) const; | ||||||
|  | 	bool isTransparent(const Point & coords) const; | ||||||
|  | 	Rect contentRect() const; | ||||||
|  | 	void draw(SDL_Surface * where, const Point & dest, const Rect * src, const ScalableImageParameters & parameters, int scalingFactor); | ||||||
|  |  | ||||||
|  | 	const SDL_Palette * getPalette() const; | ||||||
|  |  | ||||||
|  | 	std::shared_ptr<ScalableImageInstance> createImageReference(); | ||||||
|  |  | ||||||
|  | 	void preparePlayerColoredImage(PlayerColor color); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | class ScalableImageInstance final : public IImage | ||||||
|  | { | ||||||
|  | 	friend class ScalableImageShared; | ||||||
|  |  | ||||||
|  | 	std::shared_ptr<ScalableImageShared> image; | ||||||
|  |  | ||||||
|  | 	ScalableImageParameters parameters; | ||||||
|  | 	EImageBlitMode blitMode; | ||||||
|  |  | ||||||
|  | public: | ||||||
|  | 	ScalableImageInstance(const std::shared_ptr<ScalableImageShared> & image, EImageBlitMode blitMode); | ||||||
|  |  | ||||||
|  | 	void scaleTo(const Point & size) override; | ||||||
|  | 	void exportBitmap(const boost::filesystem::path & path) const override; | ||||||
|  | 	bool isTransparent(const Point & coords) const override; | ||||||
|  | 	Rect contentRect() const override; | ||||||
|  | 	Point dimensions() const override; | ||||||
|  | 	void setAlpha(uint8_t value) override; | ||||||
|  | 	void draw(SDL_Surface * where, const Point & pos, const Rect * src, int scalingFactor) const override; | ||||||
|  | 	void setOverlayColor(const ColorRGBA & color) override; | ||||||
|  | 	void playerColored(PlayerColor player) override; | ||||||
|  | 	void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; | ||||||
|  | 	void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; | ||||||
|  |  | ||||||
|  | 	void horizontalFlip(); | ||||||
|  | 	void verticalFlip(); | ||||||
|  | }; | ||||||
|  |  | ||||||
| @@ -53,8 +53,8 @@ CPicture::CPicture( const ImagePath & bmpname ) | |||||||
| 	: CPicture(bmpname, Point(0,0)) | 	: CPicture(bmpname, Point(0,0)) | ||||||
| {} | {} | ||||||
|  |  | ||||||
| CPicture::CPicture( const ImagePath & bmpname, const Point & position ) | CPicture::CPicture( const ImagePath & bmpname, const Point & position, EImageBlitMode mode ) | ||||||
| 	: bg(GH.renderHandler().loadImage(bmpname, EImageBlitMode::COLORKEY)) | 	: bg(GH.renderHandler().loadImage(bmpname, mode)) | ||||||
| 	, needRefresh(false) | 	, needRefresh(false) | ||||||
| { | { | ||||||
| 	pos.x += position.x; | 	pos.x += position.x; | ||||||
| @@ -74,6 +74,10 @@ CPicture::CPicture( const ImagePath & bmpname, const Point & position ) | |||||||
| 	addUsedEvents(SHOW_POPUP); | 	addUsedEvents(SHOW_POPUP); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | CPicture::CPicture( const ImagePath & bmpname, const Point & position ) | ||||||
|  | 	:CPicture(bmpname, position, EImageBlitMode::COLORKEY) | ||||||
|  | {} | ||||||
|  |  | ||||||
| CPicture::CPicture(const ImagePath & bmpname, const Rect &SrcRect, int x, int y) | CPicture::CPicture(const ImagePath & bmpname, const Rect &SrcRect, int x, int y) | ||||||
| 	: CPicture(bmpname, Point(x,y)) | 	: CPicture(bmpname, Point(x,y)) | ||||||
| { | { | ||||||
|   | |||||||
| @@ -21,6 +21,7 @@ class CAnimImage; | |||||||
| class CLabel; | class CLabel; | ||||||
| class CAnimation; | class CAnimation; | ||||||
| class IImage; | class IImage; | ||||||
|  | enum class EImageBlitMode : uint8_t; | ||||||
|  |  | ||||||
| // Image class | // Image class | ||||||
| class CPicture : public CIntObject | class CPicture : public CIntObject | ||||||
| @@ -49,6 +50,7 @@ public: | |||||||
|  |  | ||||||
| 	/// Loads image from specified file name | 	/// Loads image from specified file name | ||||||
| 	CPicture(const ImagePath & bmpname); | 	CPicture(const ImagePath & bmpname); | ||||||
|  | 	CPicture(const ImagePath & bmpname, const Point & position, EImageBlitMode mode); | ||||||
| 	CPicture(const ImagePath & bmpname, const Point & position); | 	CPicture(const ImagePath & bmpname, const Point & position); | ||||||
| 	CPicture(const ImagePath & bmpname, int x, int y); | 	CPicture(const ImagePath & bmpname, int x, int y); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -570,9 +570,8 @@ CCastleBuildings::CCastleBuildings(const CGTownInstance* Town): | |||||||
| { | { | ||||||
| 	OBJECT_CONSTRUCTION; | 	OBJECT_CONSTRUCTION; | ||||||
|  |  | ||||||
| 	background = std::make_shared<CPicture>(town->getTown()->clientInfo.townBackground); | 	background = std::make_shared<CPicture>(town->getTown()->clientInfo.townBackground, Point(0,0), EImageBlitMode::OPAQUE); | ||||||
| 	background->needRefresh = true; | 	background->needRefresh = true; | ||||||
| 	background->getSurface()->setBlitMode(EImageBlitMode::OPAQUE); |  | ||||||
| 	pos.w = background->pos.w; | 	pos.w = background->pos.w; | ||||||
| 	pos.h = background->pos.h; | 	pos.h = background->pos.h; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -139,9 +139,8 @@ std::shared_ptr<CPicture> CMapOverviewWidget::buildDrawMinimap(const JsonNode & | |||||||
| 	double resize = maxSideLengthSrc / maxSideLengthDst; | 	double resize = maxSideLengthSrc / maxSideLengthDst; | ||||||
| 	Point newMinimapSize = Point(minimapRect.w / resize, minimapRect.h / resize); | 	Point newMinimapSize = Point(minimapRect.w / resize, minimapRect.h / resize); | ||||||
|  |  | ||||||
| 	Canvas canvasScaled = Canvas(Point(rect.w, rect.h), CanvasScalingPolicy::AUTO); | 	std::shared_ptr<IImage> img = GH.renderHandler().createImage(minimaps[id].getInternalSurface()); | ||||||
| 	canvasScaled.drawScaled(minimaps[id], Point((rect.w - newMinimapSize.x) / 2, (rect.h - newMinimapSize.y) / 2), newMinimapSize); | 	img->scaleTo(newMinimapSize); | ||||||
| 	std::shared_ptr<IImage> img = GH.renderHandler().createImage(canvasScaled.getInternalSurface()); |  | ||||||
|  |  | ||||||
| 	return std::make_shared<CPicture>(img, Point(rect.x, rect.y)); | 	return std::make_shared<CPicture>(img, Point(rect.x, rect.y)); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -87,8 +87,7 @@ std::shared_ptr<CPicture> CWindowObject::createBg(const ImagePath & imageName, b | |||||||
| 	if(imageName.empty()) | 	if(imageName.empty()) | ||||||
| 		return nullptr; | 		return nullptr; | ||||||
|  |  | ||||||
| 	auto image = std::make_shared<CPicture>(imageName); | 	auto image = std::make_shared<CPicture>(imageName, Point(0,0), EImageBlitMode::OPAQUE); | ||||||
| 	image->getSurface()->setBlitMode(EImageBlitMode::OPAQUE); |  | ||||||
| 	if(playerColored) | 	if(playerColored) | ||||||
| 		image->setPlayerColor(LOCPLINT->playerID); | 		image->setPlayerColor(LOCPLINT->playerID); | ||||||
| 	return image; | 	return image; | ||||||
| @@ -116,8 +115,7 @@ void CWindowObject::updateShadow() | |||||||
| void CWindowObject::setShadow(bool on) | void CWindowObject::setShadow(bool on) | ||||||
| { | { | ||||||
| 	//size of shadow | 	//size of shadow | ||||||
| 	int sizeOriginal = 8; | 	int size = 8; | ||||||
| 	int size = sizeOriginal * GH.screenHandler().getScalingFactor(); |  | ||||||
|  |  | ||||||
| 	if(on == !shadowParts.empty()) | 	if(on == !shadowParts.empty()) | ||||||
| 		return; | 		return; | ||||||
| @@ -182,9 +180,9 @@ void CWindowObject::setShadow(bool on) | |||||||
| 		//FIXME: do something with this points | 		//FIXME: do something with this points | ||||||
| 		Point shadowStart; | 		Point shadowStart; | ||||||
| 		if (options & BORDERED) | 		if (options & BORDERED) | ||||||
| 			shadowStart = Point(sizeOriginal - 14, sizeOriginal - 14); | 			shadowStart = Point(size - 14, size - 14); | ||||||
| 		else | 		else | ||||||
| 			shadowStart = Point(sizeOriginal, sizeOriginal); | 			shadowStart = Point(size, size); | ||||||
|  |  | ||||||
| 		Point shadowPos; | 		Point shadowPos; | ||||||
| 		if (options & BORDERED) | 		if (options & BORDERED) | ||||||
| @@ -200,8 +198,8 @@ void CWindowObject::setShadow(bool on) | |||||||
|  |  | ||||||
| 		//create base 8x8 piece of shadow | 		//create base 8x8 piece of shadow | ||||||
| 		SDL_Surface * shadowCorner = CSDL_Ext::copySurface(shadowCornerTempl); | 		SDL_Surface * shadowCorner = CSDL_Ext::copySurface(shadowCornerTempl); | ||||||
| 		SDL_Surface * shadowBottom = CSDL_Ext::scaleSurface(shadowBottomTempl, (fullsize.x - sizeOriginal) * GH.screenHandler().getScalingFactor(), size); | 		SDL_Surface * shadowBottom = CSDL_Ext::scaleSurface(shadowBottomTempl, (fullsize.x - size), size); | ||||||
| 		SDL_Surface * shadowRight  = CSDL_Ext::scaleSurface(shadowRightTempl,  size, (fullsize.y - sizeOriginal) * GH.screenHandler().getScalingFactor()); | 		SDL_Surface * shadowRight  = CSDL_Ext::scaleSurface(shadowRightTempl,  size, (fullsize.y - size)); | ||||||
|  |  | ||||||
| 		blitAlphaCol(shadowBottom, 0); | 		blitAlphaCol(shadowBottom, 0); | ||||||
| 		blitAlphaRow(shadowRight, 0); | 		blitAlphaRow(shadowRight, 0); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user