mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	xBRZ-upscaled images now support common palette-transform effects:
- Player coloring - Flag color for map - Glue selection for combat
This commit is contained in:
		| @@ -323,33 +323,6 @@ static ColorRGBA genBorderColor(ui8 alpha, const ColorRGBA & base) | |||||||
| 	return ColorRGBA(base.r, base.g, base.b, ui8(base.a * alpha / 256)); | 	return ColorRGBA(base.r, base.g, base.b, ui8(base.a * alpha / 256)); | ||||||
| } | } | ||||||
|  |  | ||||||
| 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), |  | ||||||
| 			ui8(over.a + base.a * (255 - over.a) / 256) |  | ||||||
| 			); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void CreatureAnimation::genSpecialPalette(IImage::SpecialPalette & target) |  | ||||||
| { |  | ||||||
| 	target.resize(8); |  | ||||||
| 	target[0] = genShadow(0); |  | ||||||
| 	target[1] = genShadow(shadowAlpha / 2); |  | ||||||
| 	// colors 2 & 3 are not used in creatures |  | ||||||
| 	target[4] = genShadow(shadowAlpha); |  | ||||||
| 	target[5] = genBorderColor(getBorderStrength(elapsedTime), border); |  | ||||||
| 	target[6] = addColors(genShadow(shadowAlpha),     genBorderColor(getBorderStrength(elapsedTime), border)); |  | ||||||
| 	target[7] = addColors(genShadow(shadowAlpha / 2), genBorderColor(getBorderStrength(elapsedTime), border)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void CreatureAnimation::nextFrame(Canvas & canvas, const ColorFilter & shifter, bool facingRight) | void CreatureAnimation::nextFrame(Canvas & canvas, const ColorFilter & shifter, bool facingRight) | ||||||
| { | { | ||||||
| 	ColorRGBA shadowTest = shifter.shiftColor(genShadow(128)); | 	ColorRGBA shadowTest = shifter.shiftColor(genShadow(128)); | ||||||
| @@ -366,11 +339,12 @@ void CreatureAnimation::nextFrame(Canvas & canvas, const ColorFilter & shifter, | |||||||
|  |  | ||||||
| 	if(image) | 	if(image) | ||||||
| 	{ | 	{ | ||||||
| 		IImage::SpecialPalette SpecialPalette; | 		image->setShadowEnabled(true); | ||||||
| 		genSpecialPalette(SpecialPalette); | 		image->setOverlayEnabled(isIdle()); | ||||||
|  | 		if (isIdle()) | ||||||
|  | 			image->setOverlayColor(genBorderColor(getBorderStrength(elapsedTime), border)); | ||||||
|  |  | ||||||
| 		image->setSpecialPalette(SpecialPalette, IImage::SPECIAL_PALETTE_MASK_CREATURES); | 		image->adjustPalette(shifter, 0); | ||||||
| 		image->adjustPalette(shifter, IImage::SPECIAL_PALETTE_MASK_CREATURES); |  | ||||||
|  |  | ||||||
| 		canvas.draw(image, pos.topLeft(), Rect(0, 0, pos.w, pos.h)); | 		canvas.draw(image, pos.topLeft(), Rect(0, 0, pos.w, pos.h)); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -107,9 +107,7 @@ private: | |||||||
|  |  | ||||||
| 	void endAnimation(); | 	void endAnimation(); | ||||||
|  |  | ||||||
| 	void genSpecialPalette(IImage::SpecialPalette & target); |  | ||||||
| public: | public: | ||||||
|  |  | ||||||
| 	/// function(s) that will be called when animation ends, after reset to 1st frame | 	/// function(s) that will be called when animation ends, after reset to 1st frame | ||||||
| 	/// NOTE that these functions will be fired only once | 	/// NOTE that these functions will be fired only once | ||||||
| 	CFunctionList<void()> onAnimationReset; | 	CFunctionList<void()> onAnimationReset; | ||||||
|   | |||||||
| @@ -21,6 +21,7 @@ | |||||||
| #include "../render/IImage.h" | #include "../render/IImage.h" | ||||||
| #include "../render/IRenderHandler.h" | #include "../render/IRenderHandler.h" | ||||||
| #include "../render/Colors.h" | #include "../render/Colors.h" | ||||||
|  | #include "../render/Graphics.h" | ||||||
|  |  | ||||||
| #include "../../CCallback.h" | #include "../../CCallback.h" | ||||||
|  |  | ||||||
| @@ -477,23 +478,20 @@ void MapRendererObjects::renderImage(IMapRendererContext & context, Canvas & tar | |||||||
| 		return; | 		return; | ||||||
|  |  | ||||||
| 	image->setAlpha(transparency); | 	image->setAlpha(transparency); | ||||||
| 	image->setFlagColor(object->tempOwner); | 	image->setShadowEnabled(true); | ||||||
|  | 	image->setOverlayEnabled(object->getOwner().isValidPlayer() || object->getOwner() == PlayerColor::NEUTRAL); | ||||||
|  |  | ||||||
|  | 	if (object->getOwner().isValidPlayer()) | ||||||
|  | 		image->setOverlayColor(graphics->playerColors[object->getOwner().getNum()]); | ||||||
|  |  | ||||||
|  | 	if (object->getOwner() == PlayerColor::NEUTRAL) | ||||||
|  | 		image->setOverlayColor(graphics->neutralColor); | ||||||
|  |  | ||||||
| 	Point offsetPixels = context.objectImageOffset(object->id, coordinates); | 	Point offsetPixels = context.objectImageOffset(object->id, coordinates); | ||||||
|  |  | ||||||
| 	if ( offsetPixels.x < image->dimensions().x && offsetPixels.y < image->dimensions().y) | 	if ( offsetPixels.x < image->dimensions().x && offsetPixels.y < image->dimensions().y) | ||||||
| 	{ | 	{ | ||||||
| 		Point imagePos = image->dimensions() - offsetPixels - Point(32, 32); | 		Point imagePos = image->dimensions() - offsetPixels - Point(32, 32); | ||||||
|  |  | ||||||
| 		//if (transparency == 255) |  | ||||||
| 		//{ |  | ||||||
| 		//	Canvas intermediate(Point(32,32)); |  | ||||||
| 		//	intermediate.enableTransparency(true); |  | ||||||
| 		//	image->setBlitMode(EImageBlitMode::OPAQUE); |  | ||||||
| 		//	intermediate.draw(image, Point(0, 0), Rect(imagePos, Point(32,32))); |  | ||||||
| 		//	target.draw(intermediate, Point(0,0)); |  | ||||||
| 		//	return; |  | ||||||
| 		//} |  | ||||||
| 		target.draw(image, Point(0, 0), Rect(imagePos, Point(32,32))); | 		target.draw(image, Point(0, 0), Rect(imagePos, Point(32,32))); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -23,7 +23,7 @@ class CAnimation; | |||||||
| class IImage; | class IImage; | ||||||
| class Canvas; | class Canvas; | ||||||
| class IMapRendererContext; | class IMapRendererContext; | ||||||
| enum class EImageBlitMode; | enum class EImageBlitMode : uint8_t; | ||||||
|  |  | ||||||
| class MapTileStorage | class MapTileStorage | ||||||
| { | { | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ | |||||||
| #include "../gui/CGuiHandler.h" | #include "../gui/CGuiHandler.h" | ||||||
| #include "../render/IImage.h" | #include "../render/IImage.h" | ||||||
| #include "../render/IRenderHandler.h" | #include "../render/IRenderHandler.h" | ||||||
|  | #include "../render/IScreenHandler.h" | ||||||
|  |  | ||||||
| #include "../../lib/filesystem/Filesystem.h" | #include "../../lib/filesystem/Filesystem.h" | ||||||
| #include "../../lib/json/JsonUtils.h" | #include "../../lib/json/JsonUtils.h" | ||||||
|   | |||||||
| @@ -36,48 +36,10 @@ enum class DefType : uint32_t | |||||||
|  *  DefFile, class used for def loading                                  * |  *  DefFile, class used for def loading                                  * | ||||||
|  *************************************************************************/ |  *************************************************************************/ | ||||||
|  |  | ||||||
| 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; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| CDefFile::CDefFile(const AnimationPath & Name): | CDefFile::CDefFile(const AnimationPath & Name): | ||||||
| 	data(nullptr), | 	data(nullptr), | ||||||
| 	palette(nullptr) | 	palette(nullptr) | ||||||
| { | { | ||||||
| 	//First 8 colors in def palette used for transparency |  | ||||||
| 	static const SDL_Color sourcePalette[8] = { |  | ||||||
| 		{0,   255, 255, SDL_ALPHA_OPAQUE}, |  | ||||||
| 		{255, 150, 255, SDL_ALPHA_OPAQUE}, |  | ||||||
| 		{255, 100, 255, SDL_ALPHA_OPAQUE}, |  | ||||||
| 		{255, 50,  255, SDL_ALPHA_OPAQUE}, |  | ||||||
| 		{255, 0,   255, SDL_ALPHA_OPAQUE}, |  | ||||||
| 		{255, 255, 0,   SDL_ALPHA_OPAQUE}, |  | ||||||
| 		{180, 0,   255, SDL_ALPHA_OPAQUE}, |  | ||||||
| 		{0,   255, 0,   SDL_ALPHA_OPAQUE} |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	static const SDL_Color targetPalette[8] = { |  | ||||||
| 		{0, 0, 0, 0  }, // transparency                  ( used in most images ) |  | ||||||
| 		{0, 0, 0, 64 }, // shadow border                 ( used in battle, adventure map def's ) |  | ||||||
| 		{0, 0, 0, 64 }, // shadow border                 ( used in fog-of-war def's ) |  | ||||||
| 		{0, 0, 0, 128}, // shadow body                   ( used in fog-of-war def's ) |  | ||||||
| 		{0, 0, 0, 128}, // shadow body                   ( used in battle, adventure map def's ) |  | ||||||
| 		{0, 0, 0, 0  }, // selection / owner flag        ( used in battle, adventure map def's ) |  | ||||||
| 		{0, 0, 0, 128}, // shadow body   below selection ( used in battle def's ) |  | ||||||
| 		{0, 0, 0, 64 }  // shadow border below selection ( used in battle def's ) |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	data = CResourceHandler::get()->load(Name)->readAll().first; | 	data = CResourceHandler::get()->load(Name)->readAll().first; | ||||||
|  |  | ||||||
| 	palette = std::unique_ptr<SDL_Color[]>(new SDL_Color[256]); | 	palette = std::unique_ptr<SDL_Color[]>(new SDL_Color[256]); | ||||||
| @@ -99,18 +61,6 @@ CDefFile::CDefFile(const AnimationPath & Name): | |||||||
| 		palette[i].a = SDL_ALPHA_OPAQUE; | 		palette[i].a = SDL_ALPHA_OPAQUE; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// these colors seems to be used unconditionally |  | ||||||
| 	palette[0] = targetPalette[0]; |  | ||||||
| 	palette[1] = targetPalette[1]; |  | ||||||
| 	palette[4] = targetPalette[4]; |  | ||||||
|  |  | ||||||
| 	// rest of special colors are used only if their RGB values are close to H3 |  | ||||||
| 	for (uint32_t i = 0; i < 8; ++i) |  | ||||||
| 	{ |  | ||||||
| 		if (colorsSimilar(sourcePalette[i], palette[i])) |  | ||||||
| 			palette[i] = targetPalette[i]; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for (ui32 i=0; i<totalBlocks; i++) | 	for (ui32 i=0; i<totalBlocks; i++) | ||||||
| 	{ | 	{ | ||||||
| 		size_t blockID = read_le_u32(data.get() + it); | 		size_t blockID = read_le_u32(data.get() + it); | ||||||
|   | |||||||
| @@ -15,6 +15,7 @@ | |||||||
|  |  | ||||||
| const ColorRGBA Colors::YELLOW = { 229, 215, 123, ColorRGBA::ALPHA_OPAQUE }; | const ColorRGBA Colors::YELLOW = { 229, 215, 123, ColorRGBA::ALPHA_OPAQUE }; | ||||||
| const ColorRGBA Colors::WHITE = { 255, 243, 222, ColorRGBA::ALPHA_OPAQUE }; | const ColorRGBA Colors::WHITE = { 255, 243, 222, ColorRGBA::ALPHA_OPAQUE }; | ||||||
|  | const ColorRGBA Colors::WHITE_TRUE = { 255, 255, 255, ColorRGBA::ALPHA_OPAQUE }; | ||||||
| const ColorRGBA Colors::METALLIC_GOLD = { 173, 142, 66, ColorRGBA::ALPHA_OPAQUE }; | const ColorRGBA Colors::METALLIC_GOLD = { 173, 142, 66, ColorRGBA::ALPHA_OPAQUE }; | ||||||
| const ColorRGBA Colors::GREEN = { 0, 255, 0, ColorRGBA::ALPHA_OPAQUE }; | const ColorRGBA Colors::GREEN = { 0, 255, 0, ColorRGBA::ALPHA_OPAQUE }; | ||||||
| const ColorRGBA Colors::CYAN = { 0, 255, 255, ColorRGBA::ALPHA_OPAQUE }; | const ColorRGBA Colors::CYAN = { 0, 255, 255, ColorRGBA::ALPHA_OPAQUE }; | ||||||
|   | |||||||
| @@ -23,6 +23,9 @@ public: | |||||||
| 	/** the standard h3 white color */ | 	/** the standard h3 white color */ | ||||||
| 	static const ColorRGBA WHITE; | 	static const ColorRGBA WHITE; | ||||||
|  |  | ||||||
|  | 	/** actual 100% white color */ | ||||||
|  | 	static const ColorRGBA WHITE_TRUE; | ||||||
|  |  | ||||||
| 	/** the metallic gold color used mostly as a border around buttons */ | 	/** the metallic gold color used mostly as a border around buttons */ | ||||||
| 	static const ColorRGBA METALLIC_GOLD; | 	static const ColorRGBA METALLIC_GOLD; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -23,9 +23,10 @@ VCMI_LIB_NAMESPACE_END | |||||||
| struct SDL_Surface; | struct SDL_Surface; | ||||||
| struct SDL_Palette; | struct SDL_Palette; | ||||||
| class ColorFilter; | class ColorFilter; | ||||||
|  | class ISharedImage; | ||||||
|  |  | ||||||
| /// Defines which blit method will be selected when image is used for rendering | /// Defines which blit method will be selected when image is used for rendering | ||||||
| enum class EImageBlitMode | enum class EImageBlitMode : uint8_t | ||||||
| { | { | ||||||
| 	/// Preferred for images that don't need any background | 	/// Preferred for images that don't need any background | ||||||
| 	/// Indexed or RGBA: Image can have no transparency and can be only used as background | 	/// Indexed or RGBA: Image can have no transparency and can be only used as background | ||||||
| @@ -41,15 +42,11 @@ enum class EImageBlitMode | |||||||
| 	ALPHA | 	ALPHA | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* | /// Base class for images for use in client code. | ||||||
|  * Base class for images, can be used for non-animation pictures as well | /// This class represents current state of image, with potential transformations applied, such as player coloring | ||||||
|  */ |  | ||||||
| class IImage | class IImage | ||||||
| { | { | ||||||
| public: | public: | ||||||
| 	using SpecialPalette = std::vector<ColorRGBA>; |  | ||||||
| 	static constexpr int32_t SPECIAL_PALETTE_MASK_CREATURES = 0b11110011; |  | ||||||
|  |  | ||||||
| 	//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 = nullptr) const = 0; | ||||||
|  |  | ||||||
| @@ -60,9 +57,6 @@ public: | |||||||
| 	//Change palette to specific player | 	//Change palette to specific player | ||||||
| 	virtual void playerColored(PlayerColor player) = 0; | 	virtual void playerColored(PlayerColor player) = 0; | ||||||
|  |  | ||||||
| 	//set special color for flag |  | ||||||
| 	virtual void setFlagColor(PlayerColor player) = 0; |  | ||||||
|  |  | ||||||
| 	//test transparency of specific pixel | 	//test transparency of specific pixel | ||||||
| 	virtual bool isTransparent(const Point & coords) const = 0; | 	virtual bool isTransparent(const Point & coords) const = 0; | ||||||
|  |  | ||||||
| @@ -78,23 +72,32 @@ public: | |||||||
| 	virtual void setBlitMode(EImageBlitMode mode) = 0; | 	virtual void setBlitMode(EImageBlitMode mode) = 0; | ||||||
|  |  | ||||||
| 	//only indexed bitmaps with 7 special colors | 	//only indexed bitmaps with 7 special colors | ||||||
| 	virtual void setSpecialPalette(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) = 0; | 	virtual void setOverlayColor(const ColorRGBA & color) = 0; | ||||||
|  |  | ||||||
|  | 	virtual void setShadowEnabled(bool on) = 0; | ||||||
|  | 	virtual void setBodyEnabled(bool on) = 0; | ||||||
|  | 	virtual void setOverlayEnabled(bool on) = 0; | ||||||
|  | 	virtual std::shared_ptr<ISharedImage> getSharedImage() const = 0; | ||||||
|  |  | ||||||
| 	virtual ~IImage() = default; | 	virtual ~IImage() = default; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | /// Base class for image data, mostly for internal use | ||||||
|  | /// Represents unmodified pixel data, usually loaded from file | ||||||
|  | /// This image can be shared between multiple image handlers (IImage instances) | ||||||
| class ISharedImage | class ISharedImage | ||||||
| { | { | ||||||
| public: | public: | ||||||
| 	virtual Point dimensions() const = 0; | 	virtual Point dimensions() const = 0; | ||||||
| 	virtual void exportBitmap(const boost::filesystem::path & path) const = 0; | 	virtual void exportBitmap(const boost::filesystem::path & path) const = 0; | ||||||
| 	virtual bool isTransparent(const Point & coords) const = 0; | 	virtual bool isTransparent(const Point & coords) const = 0; | ||||||
| 	virtual void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, 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; | ||||||
|  |  | ||||||
| 	virtual std::shared_ptr<IImage> createImageReference(EImageBlitMode mode) = 0; | 	virtual std::shared_ptr<IImage> createImageReference(EImageBlitMode mode) = 0; | ||||||
|  |  | ||||||
| 	virtual std::shared_ptr<ISharedImage> horizontalFlip() const = 0; | 	virtual std::shared_ptr<ISharedImage> horizontalFlip() const = 0; | ||||||
| 	virtual std::shared_ptr<ISharedImage> verticalFlip() const = 0; | 	virtual std::shared_ptr<ISharedImage> verticalFlip() const = 0; | ||||||
|  | 	virtual std::shared_ptr<ISharedImage> scaleFast(const Point & size, SDL_Palette * palette) const = 0; | ||||||
|  |  | ||||||
|  |  | ||||||
| 	virtual ~ISharedImage() = default; | 	virtual ~ISharedImage() = default; | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ struct SDL_Surface; | |||||||
|  |  | ||||||
| class IImage; | class IImage; | ||||||
| class CAnimation; | class CAnimation; | ||||||
| enum class EImageBlitMode; | enum class EImageBlitMode : uint8_t; | ||||||
|  |  | ||||||
| class IRenderHandler : public boost::noncopyable | class IRenderHandler : public boost::noncopyable | ||||||
| { | { | ||||||
|   | |||||||
| @@ -10,6 +10,9 @@ | |||||||
| #include "StdInc.h" | #include "StdInc.h" | ||||||
| #include "ImageLocator.h" | #include "ImageLocator.h" | ||||||
|  |  | ||||||
|  | #include "../gui/CGuiHandler.h" | ||||||
|  | #include "IScreenHandler.h" | ||||||
|  |  | ||||||
| #include "../../lib/json/JsonNode.h" | #include "../../lib/json/JsonNode.h" | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -47,10 +50,46 @@ bool ImageLocator::operator<(const ImageLocator & other) const | |||||||
| 		return defFrame < other.defFrame; | 		return defFrame < other.defFrame; | ||||||
| 	if(verticalFlip != other.verticalFlip) | 	if(verticalFlip != other.verticalFlip) | ||||||
| 		return verticalFlip < other.verticalFlip; | 		return verticalFlip < other.verticalFlip; | ||||||
| 	return horizontalFlip < other.horizontalFlip; | 	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(layerShadow != other.layerShadow) | ||||||
|  | 		return layerShadow < other.layerShadow; | ||||||
|  | 	if(layerBody != other.layerBody) | ||||||
|  | 		return layerBody < other.layerBody; | ||||||
|  | 	if (layerOverlay != other.layerOverlay) | ||||||
|  | 		return layerOverlay < other.layerOverlay; | ||||||
|  |  | ||||||
|  | 	return false; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool ImageLocator::empty() const | 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.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 | ||||||
|  | } | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "../../lib/filesystem/ResourcePath.h" | #include "../../lib/filesystem/ResourcePath.h" | ||||||
|  | #include "../../lib/constants/EntityIdentifiers.h" | ||||||
|  |  | ||||||
| struct ImageLocator | struct ImageLocator | ||||||
| { | { | ||||||
| @@ -20,6 +21,11 @@ struct ImageLocator | |||||||
|  |  | ||||||
| 	bool verticalFlip = false; | 	bool verticalFlip = false; | ||||||
| 	bool horizontalFlip = false; | 	bool horizontalFlip = false; | ||||||
|  | 	int8_t scalingFactor = 1; | ||||||
|  | 	PlayerColor playerColored = PlayerColor::CANNOT_DETERMINE; | ||||||
|  | 	bool layerShadow = false; | ||||||
|  | 	bool layerBody = true; | ||||||
|  | 	bool layerOverlay = false; | ||||||
|  |  | ||||||
| 	ImageLocator() = default; | 	ImageLocator() = default; | ||||||
| 	ImageLocator(const AnimationPath & path, int frame, int group); | 	ImageLocator(const AnimationPath & path, int frame, int group); | ||||||
| @@ -28,4 +34,8 @@ struct ImageLocator | |||||||
|  |  | ||||||
| 	bool operator < (const ImageLocator & other) const; | 	bool operator < (const ImageLocator & other) const; | ||||||
| 	bool empty() const; | 	bool empty() const; | ||||||
|  |  | ||||||
|  | 	ImageLocator copyFile() const; | ||||||
|  | 	ImageLocator copyFileTransform() const; | ||||||
|  | 	ImageLocator copyFileTransformScale() const; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -13,88 +13,49 @@ | |||||||
| #include "SDLImage.h" | #include "SDLImage.h" | ||||||
| #include "SDL_Extensions.h" | #include "SDL_Extensions.h" | ||||||
|  |  | ||||||
|  | #include "../gui/CGuiHandler.h" | ||||||
|  | #include "../render/IScreenHandler.h" | ||||||
|  | #include "../render/Colors.h" | ||||||
|  |  | ||||||
| #include "../../lib/constants/EntityIdentifiers.h" | #include "../../lib/constants/EntityIdentifiers.h" | ||||||
|  |  | ||||||
| #include <SDL_surface.h> | #include <SDL_surface.h> | ||||||
|  |  | ||||||
| ImageSharedScaled::ImageSharedScaled(std::shared_ptr<SDLImageShared> sourceImage) | ImageScaled::ImageScaled(const ImageLocator & inputLocator, const std::shared_ptr<ISharedImage> & source, EImageBlitMode mode) | ||||||
| 	:sourceImage(sourceImage) | 	: source(source) | ||||||
| { | 	, locator(inputLocator) | ||||||
| 	scaledImage = sourceImage->scaleFast(sourceImage->dimensions() * getScalingFactor()); | 	, colorMultiplier(Colors::WHITE_TRUE) | ||||||
| } |  | ||||||
|  |  | ||||||
| int ImageSharedScaled::getScalingFactor() const |  | ||||||
| { |  | ||||||
| 	return 2; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ImageSharedScaled::draw(SDL_Surface *where, SDL_Palette * palette, const Point &dest, const Rect *src, uint8_t alpha, EImageBlitMode mode) const |  | ||||||
| { |  | ||||||
| 	scaledImage->draw(where, nullptr, dest, src, alpha, mode); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void ImageSharedScaled::exportBitmap(const boost::filesystem::path &path) const |  | ||||||
| { |  | ||||||
| 	sourceImage->exportBitmap(path); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Point ImageSharedScaled::dimensions() const |  | ||||||
| { |  | ||||||
| 	return sourceImage->dimensions(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool ImageSharedScaled::isTransparent(const Point &coords) const |  | ||||||
| { |  | ||||||
| 	return sourceImage->isTransparent(coords); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::shared_ptr<IImage> ImageSharedScaled::createImageReference(EImageBlitMode mode) |  | ||||||
| { |  | ||||||
| 	return std::make_shared<ImageScaled>(shared_from_this(), mode); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::shared_ptr<ISharedImage> ImageSharedScaled::horizontalFlip() const |  | ||||||
| { |  | ||||||
| 	return std::make_shared<ImageSharedScaled>(std::dynamic_pointer_cast<SDLImageShared>(sourceImage->horizontalFlip())); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::shared_ptr<ISharedImage> ImageSharedScaled::verticalFlip() const |  | ||||||
| { |  | ||||||
| 	return std::make_shared<ImageSharedScaled>(std::dynamic_pointer_cast<SDLImageShared>(sourceImage->verticalFlip())); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::shared_ptr<ImageSharedScaled> ImageSharedScaled::scaleFast(const Point &size) const |  | ||||||
| { |  | ||||||
| 	return std::make_shared<ImageSharedScaled>(sourceImage->scaleFast(size)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /////////////////////////////////////////////////////////////////////////////////////////////////// |  | ||||||
|  |  | ||||||
| ImageScaled::ImageScaled(const std::shared_ptr<ImageSharedScaled> &image, EImageBlitMode mode) |  | ||||||
| 	:image(image) |  | ||||||
| 	, alphaValue(SDL_ALPHA_OPAQUE) | 	, alphaValue(SDL_ALPHA_OPAQUE) | ||||||
| 	, blitMode(mode) | 	, blitMode(mode) | ||||||
| { | { | ||||||
|  | 	locator.scalingFactor = GH.screenHandler().getScalingFactor(); | ||||||
|  | 	setBodyEnabled(true); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::shared_ptr<ISharedImage> ImageScaled::getSharedImage() const | ||||||
|  | { | ||||||
|  | 	return body; | ||||||
| } | } | ||||||
|  |  | ||||||
| void ImageScaled::scaleFast(const Point &size) | void ImageScaled::scaleFast(const Point &size) | ||||||
| { | { | ||||||
| 	image = image->scaleFast(size); | 	if (body) | ||||||
|  | 		body = body->scaleFast(size, nullptr); // FIXME: adjust for scaling | ||||||
| } | } | ||||||
|  |  | ||||||
| void ImageScaled::exportBitmap(const boost::filesystem::path &path) const | void ImageScaled::exportBitmap(const boost::filesystem::path &path) const | ||||||
| { | { | ||||||
| 	image->exportBitmap(path); | 	source->exportBitmap(path); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool ImageScaled::isTransparent(const Point &coords) const | bool ImageScaled::isTransparent(const Point &coords) const | ||||||
| { | { | ||||||
| 	return image->isTransparent(coords); | 	return source->isTransparent(coords); | ||||||
| } | } | ||||||
|  |  | ||||||
| Point ImageScaled::dimensions() const | Point ImageScaled::dimensions() const | ||||||
| { | { | ||||||
| 	return image->dimensions(); | 	return source->dimensions(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void ImageScaled::setAlpha(uint8_t value) | void ImageScaled::setAlpha(uint8_t value) | ||||||
| @@ -109,25 +70,75 @@ void ImageScaled::setBlitMode(EImageBlitMode mode) | |||||||
|  |  | ||||||
| void ImageScaled::draw(SDL_Surface *where, const Point &pos, const Rect *src) const | void ImageScaled::draw(SDL_Surface *where, const Point &pos, const Rect *src) const | ||||||
| { | { | ||||||
| 	image->draw(where, pos, src, alphaValue, blitMode); | 	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::setSpecialPalette(const SpecialPalette &SpecialPalette, uint32_t colorsToSkipMask) | void ImageScaled::setOverlayColor(const ColorRGBA & color) | ||||||
| { | { | ||||||
|  | 	colorMultiplier = color; | ||||||
| } | } | ||||||
|  |  | ||||||
| void ImageScaled::playerColored(PlayerColor player) | void ImageScaled::playerColored(PlayerColor player) | ||||||
| { | { | ||||||
| } | 	playerColor = player; | ||||||
|  | 	if (body) | ||||||
| void ImageScaled::setFlagColor(PlayerColor player) | 		setBodyEnabled(true); // regenerate | ||||||
| { |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void ImageScaled::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) | void ImageScaled::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) | ||||||
| { | { | ||||||
|  | 	// TODO: implement | ||||||
| } | } | ||||||
|  |  | ||||||
| void ImageScaled::adjustPalette(const ColorFilter &shifter, uint32_t colorsToSkipMask) | void ImageScaled::adjustPalette(const ColorFilter &shifter, uint32_t colorsToSkipMask) | ||||||
| { | { | ||||||
|  | 	// TODO: implement | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ImageScaled::setShadowEnabled(bool on) | ||||||
|  | { | ||||||
|  | 	if (on) | ||||||
|  | 	{ | ||||||
|  | 		locator.layerBody = false; | ||||||
|  | 		locator.layerShadow = true; | ||||||
|  | 		locator.layerOverlay = false; | ||||||
|  | 		locator.playerColored = PlayerColor::CANNOT_DETERMINE; | ||||||
|  | 		shadow = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage(); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		shadow = nullptr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ImageScaled::setBodyEnabled(bool on) | ||||||
|  | { | ||||||
|  | 	if (on) | ||||||
|  | 	{ | ||||||
|  | 		locator.layerBody = true; | ||||||
|  | 		locator.layerShadow = false; | ||||||
|  | 		locator.layerOverlay = false; | ||||||
|  | 		locator.playerColored = playerColor; | ||||||
|  | 		body = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage(); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		body = nullptr; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | void ImageScaled::setOverlayEnabled(bool on) | ||||||
|  | { | ||||||
|  | 	if (on) | ||||||
|  | 	{ | ||||||
|  | 		locator.layerBody = false; | ||||||
|  | 		locator.layerShadow = false; | ||||||
|  | 		locator.layerOverlay = true; | ||||||
|  | 		locator.playerColored = PlayerColor::CANNOT_DETERMINE; | ||||||
|  | 		overlay = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage(); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 		overlay = nullptr; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -12,40 +12,40 @@ | |||||||
| #include "../render/IImage.h" | #include "../render/IImage.h" | ||||||
| #include "../render/IRenderHandler.h" | #include "../render/IRenderHandler.h" | ||||||
|  |  | ||||||
|  | #include "../../lib/Color.h" | ||||||
|  | #include "../../lib/constants/EntityIdentifiers.h" | ||||||
|  |  | ||||||
| struct SDL_Palette; | struct SDL_Palette; | ||||||
|  |  | ||||||
| class SDLImageShared; | class SDLImageShared; | ||||||
|  |  | ||||||
| class ImageSharedScaled final : public ISharedImage, public std::enable_shared_from_this<ImageSharedScaled>, boost::noncopyable | // Upscaled image with several mechanisms to emulate H3 palette effects | ||||||
| { |  | ||||||
| 	std::shared_ptr<SDLImageShared> sourceImage; |  | ||||||
| 	std::shared_ptr<SDLImageShared> scaledImage; |  | ||||||
|  |  | ||||||
| 	int getScalingFactor() const; |  | ||||||
| public: |  | ||||||
| 	ImageSharedScaled(std::shared_ptr<SDLImageShared> sourceImage); |  | ||||||
|  |  | ||||||
| 	void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, uint8_t alpha, EImageBlitMode mode) const override; |  | ||||||
|  |  | ||||||
| 	void exportBitmap(const boost::filesystem::path & path) const override; |  | ||||||
| 	Point dimensions() const override; |  | ||||||
| 	bool isTransparent(const Point & coords) const override; |  | ||||||
| 	std::shared_ptr<IImage> createImageReference(EImageBlitMode mode) override; |  | ||||||
| 	std::shared_ptr<ISharedImage> horizontalFlip() const override; |  | ||||||
| 	std::shared_ptr<ISharedImage> verticalFlip() const override; |  | ||||||
| 	std::shared_ptr<ImageSharedScaled> scaleFast(const Point & size) const; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| class ImageScaled final : public IImage | class ImageScaled final : public IImage | ||||||
| { | { | ||||||
| private: | private: | ||||||
| 	std::shared_ptr<ImageSharedScaled> image; |  | ||||||
|  | 	/// Original unscaled image | ||||||
|  | 	std::shared_ptr<ISharedImage> source; | ||||||
|  |  | ||||||
|  | 	/// Upscaled shadow of our image, may be null | ||||||
|  | 	std::shared_ptr<ISharedImage> shadow; | ||||||
|  |  | ||||||
|  | 	/// Upscaled main part of our image, may be null | ||||||
|  | 	std::shared_ptr<ISharedImage> body; | ||||||
|  |  | ||||||
|  | 	/// Upscaled overlay (player color, selection highlight) of our image, may be null | ||||||
|  | 	std::shared_ptr<ISharedImage> overlay; | ||||||
|  |  | ||||||
|  | 	ImageLocator locator; | ||||||
|  |  | ||||||
|  | 	ColorRGBA colorMultiplier; | ||||||
|  | 	PlayerColor playerColor = PlayerColor::CANNOT_DETERMINE; | ||||||
|  |  | ||||||
| 	uint8_t alphaValue; | 	uint8_t alphaValue; | ||||||
| 	EImageBlitMode blitMode; | 	EImageBlitMode blitMode; | ||||||
|  |  | ||||||
| public: | public: | ||||||
| 	ImageScaled(const std::shared_ptr<ImageSharedScaled> & image, EImageBlitMode mode); | 	ImageScaled(const ImageLocator & locator, const std::shared_ptr<ISharedImage> & source, EImageBlitMode mode); | ||||||
|  |  | ||||||
| 	void scaleFast(const Point & size) override; | 	void scaleFast(const Point & size) override; | ||||||
| 	void exportBitmap(const boost::filesystem::path & path) const override; | 	void exportBitmap(const boost::filesystem::path & path) const override; | ||||||
| @@ -54,9 +54,13 @@ public: | |||||||
| 	void setAlpha(uint8_t value) override; | 	void setAlpha(uint8_t value) override; | ||||||
| 	void setBlitMode(EImageBlitMode mode) override; | 	void setBlitMode(EImageBlitMode mode) override; | ||||||
| 	void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override; | 	void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override; | ||||||
| 	void setSpecialPalette(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) override; | 	void setOverlayColor(const ColorRGBA & color) override; | ||||||
| 	void playerColored(PlayerColor player) override; | 	void playerColored(PlayerColor player) override; | ||||||
| 	void setFlagColor(PlayerColor player) override; |  | ||||||
| 	void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; | 	void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; | ||||||
| 	void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; | 	void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; | ||||||
|  |  | ||||||
|  | 	void setShadowEnabled(bool on) override; | ||||||
|  | 	void setBodyEnabled(bool on) override; | ||||||
|  | 	void setOverlayEnabled(bool on) override; | ||||||
|  | 	std::shared_ptr<ISharedImage> getSharedImage() const override; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -15,6 +15,8 @@ | |||||||
|  |  | ||||||
| #include "../render/CAnimation.h" | #include "../render/CAnimation.h" | ||||||
| #include "../render/CDefFile.h" | #include "../render/CDefFile.h" | ||||||
|  | #include "../render/Colors.h" | ||||||
|  | #include "../render/ColorFilter.h" | ||||||
|  |  | ||||||
| #include "../../lib/json/JsonUtils.h" | #include "../../lib/json/JsonUtils.h" | ||||||
| #include "../../lib/filesystem/Filesystem.h" | #include "../../lib/filesystem/Filesystem.h" | ||||||
| @@ -131,47 +133,28 @@ int RenderHandler::getScalingFactor() const | |||||||
| 	return 2; | 	return 2; | ||||||
| } | } | ||||||
|  |  | ||||||
| std::shared_ptr<ISharedImage> RenderHandler::createScaledImage(std::shared_ptr<SDLImageShared> input) | std::shared_ptr<IImage> RenderHandler::createImageReference(const ImageLocator & locator, std::shared_ptr<ISharedImage> input, EImageBlitMode mode) | ||||||
| { | { | ||||||
| 	if (getScalingFactor() == 1) | 	if (getScalingFactor() == 1 || locator.scalingFactor != 1 || locator.empty()) | ||||||
| 		return input; | 		return input->createImageReference(mode); | ||||||
|  | 	else | ||||||
| 	return std::make_shared<ImageSharedScaled>(input); | 		return std::make_shared<ImageScaled>(locator, input, mode); | ||||||
| } | } | ||||||
|  |  | ||||||
| std::shared_ptr<ISharedImage> RenderHandler::loadImageFromSingleFile(const ImagePath & path) | ImageLocator RenderHandler::getLocatorForAnimationFrame(const AnimationPath & path, int frame, int group) | ||||||
| { |  | ||||||
| 	auto result = createScaledImage(std::make_shared<SDLImageShared>(path)); |  | ||||||
| 	imageFiles[ImageLocator(path)] = result; |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::shared_ptr<ISharedImage> RenderHandler::loadImageFromAnimationFileUncached(const AnimationPath & path, int frame, int group) |  | ||||||
| { | { | ||||||
| 	const auto & layout = getAnimationLayout(path); | 	const auto & layout = getAnimationLayout(path); | ||||||
| 	if (!layout.count(group)) | 	if (!layout.count(group)) | ||||||
| 		return loadImageFromSingleFile(ImagePath::builtin("DEFAULT")); | 		return ImageLocator(ImagePath::builtin("DEFAULT")); | ||||||
|  |  | ||||||
| 	if (frame >= layout.at(group).size()) | 	if (frame >= layout.at(group).size()) | ||||||
| 		return loadImageFromSingleFile(ImagePath::builtin("DEFAULT")); | 		return ImageLocator(ImagePath::builtin("DEFAULT")); | ||||||
|  |  | ||||||
| 	const auto & locator = layout.at(group).at(frame); | 	const auto & locator = layout.at(group).at(frame); | ||||||
| 	if (locator.image) | 	if (locator.image || locator.defFile) | ||||||
| 	{ | 		return locator; | ||||||
| 		return loadImageImpl(locator); |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 	{ |  | ||||||
| 		auto defFile = getAnimationFile(path); |  | ||||||
| 		return createScaledImage(std::make_shared<SDLImageShared>(defFile.get(), frame, group)); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::shared_ptr<ISharedImage> RenderHandler::loadImageFromAnimationFile(const AnimationPath & path, int frame, int group) | 	return ImageLocator(path, frame, group); | ||||||
| { |  | ||||||
| 	auto result = loadImageFromAnimationFileUncached(path, frame, group); |  | ||||||
| 	imageFiles[ImageLocator(path, frame, group)] = result; |  | ||||||
| 	return result; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| std::shared_ptr<ISharedImage> RenderHandler::loadImageImpl(const ImageLocator & locator) | std::shared_ptr<ISharedImage> RenderHandler::loadImageImpl(const ImageLocator & locator) | ||||||
| @@ -180,15 +163,52 @@ std::shared_ptr<ISharedImage> RenderHandler::loadImageImpl(const ImageLocator & | |||||||
| 	if (it != imageFiles.end()) | 	if (it != imageFiles.end()) | ||||||
| 		return it->second; | 		return it->second; | ||||||
|  |  | ||||||
| 	std::shared_ptr<ISharedImage> result; | 	// TODO: order should be different: | ||||||
|  | 	// 1) try to find correctly scaled image | ||||||
|  | 	// 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); | ||||||
|  |  | ||||||
|  | 	return scaledImage; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::shared_ptr<ISharedImage> RenderHandler::loadImageFromFileUncached(const ImageLocator & locator) | ||||||
|  | { | ||||||
| 	if (locator.image) | 	if (locator.image) | ||||||
| 		result = loadImageFromSingleFile(*locator.image); | 	{ | ||||||
| 	else if (locator.defFile) | 		// TODO: create EmptySharedImage class that will be instantiated if image does not exists or fails to load | ||||||
| 		result = loadImageFromAnimationFile(*locator.defFile, locator.defFrame, locator.defGroup); | 		return std::make_shared<SDLImageShared>(*locator.image); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if (!result) | 	if (locator.defFile) | ||||||
| 		result = loadImageFromSingleFile(ImagePath::builtin("DEFAULT")); | 	{ | ||||||
|  | 		auto defFile = getAnimationFile(*locator.defFile); | ||||||
|  | 		return std::make_shared<SDLImageShared>(defFile.get(), locator.defFrame, locator.defGroup); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	throw std::runtime_error("Invalid image locator received!"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::shared_ptr<ISharedImage> RenderHandler::loadImageFromFile(const ImageLocator & locator) | ||||||
|  | { | ||||||
|  | 	if (imageFiles.count(locator)) | ||||||
|  | 		return imageFiles.at(locator); | ||||||
|  |  | ||||||
|  | 	auto result = loadImageFromFileUncached(locator); | ||||||
|  | 	imageFiles[locator] = result; | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::shared_ptr<ISharedImage> RenderHandler::transformImage(const ImageLocator & locator, std::shared_ptr<ISharedImage> image) | ||||||
|  | { | ||||||
|  | 	if (imageFiles.count(locator)) | ||||||
|  | 		return imageFiles.at(locator); | ||||||
|  |  | ||||||
|  | 	auto result = image; | ||||||
|  |  | ||||||
| 	if (locator.verticalFlip) | 	if (locator.verticalFlip) | ||||||
| 		result = result->verticalFlip(); | 		result = result->verticalFlip(); | ||||||
| @@ -200,24 +220,50 @@ std::shared_ptr<ISharedImage> RenderHandler::loadImageImpl(const ImageLocator & | |||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | std::shared_ptr<ISharedImage> RenderHandler::scaleImage(const ImageLocator & locator, std::shared_ptr<ISharedImage> image) | ||||||
|  | { | ||||||
|  | 	if (imageFiles.count(locator)) | ||||||
|  | 		return imageFiles.at(locator); | ||||||
|  |  | ||||||
|  | 	auto handle = image->createImageReference(EImageBlitMode::OPAQUE); | ||||||
|  |  | ||||||
|  | 	assert(locator.scalingFactor != 1); // should be filtered-out before | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	handle->setOverlayEnabled(locator.layerOverlay); | ||||||
|  | 	handle->setBodyEnabled(locator.layerBody); | ||||||
|  | 	handle->setShadowEnabled(locator.layerShadow); | ||||||
|  | 	if (locator.layerBody && locator.playerColored != PlayerColor::CANNOT_DETERMINE) | ||||||
|  | 		handle->playerColored(locator.playerColored); | ||||||
|  |  | ||||||
|  | 	handle->scaleFast(handle->dimensions() * locator.scalingFactor); | ||||||
|  |  | ||||||
|  | 	// TODO: try to optimize image size (possibly even before scaling?) - trim image borders if they are completely transparent | ||||||
|  | 	auto result = handle->getSharedImage(); | ||||||
|  | 	imageFiles[locator] = result; | ||||||
|  | 	return result; | ||||||
|  | } | ||||||
|  |  | ||||||
| std::shared_ptr<IImage> RenderHandler::loadImage(const ImageLocator & locator, EImageBlitMode mode) | std::shared_ptr<IImage> RenderHandler::loadImage(const ImageLocator & locator, EImageBlitMode mode) | ||||||
| { | { | ||||||
| 	return loadImageImpl(locator)->createImageReference(mode); | 	return createImageReference(locator, loadImageImpl(locator), mode); | ||||||
| } | } | ||||||
|  |  | ||||||
| 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) | ||||||
| { | { | ||||||
| 	return loadImageFromAnimationFile(path, frame, group)->createImageReference(mode); | 	auto locator = getLocatorForAnimationFrame(path, frame, group); | ||||||
|  | 	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) | ||||||
| { | { | ||||||
| 	return loadImageImpl(ImageLocator(path))->createImageReference(mode); | 	return loadImage(ImageLocator(path), mode); | ||||||
| } | } | ||||||
|  |  | ||||||
| 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::ALPHA); | 	return createImageReference(ImageLocator(), std::make_shared<SDLImageShared>(source), EImageBlitMode::ALPHA); | ||||||
| } | } | ||||||
|  |  | ||||||
| std::shared_ptr<CAnimation> RenderHandler::loadAnimation(const AnimationPath & path, EImageBlitMode mode) | std::shared_ptr<CAnimation> RenderHandler::loadAnimation(const AnimationPath & path, EImageBlitMode mode) | ||||||
|   | |||||||
| @@ -34,14 +34,19 @@ class RenderHandler : public IRenderHandler | |||||||
| 	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); | ||||||
|  |  | ||||||
| 	std::shared_ptr<ISharedImage> loadImageFromSingleFile(const ImagePath & path); |  | ||||||
| 	std::shared_ptr<ISharedImage> loadImageFromAnimationFileUncached(const AnimationPath & path, int frame, int group); |  | ||||||
| 	std::shared_ptr<ISharedImage> loadImageFromAnimationFile(const AnimationPath & path, int frame, int group); |  | ||||||
| 	std::shared_ptr<ISharedImage> loadImageImpl(const ImageLocator & config); | 	std::shared_ptr<ISharedImage> loadImageImpl(const ImageLocator & config); | ||||||
|  |  | ||||||
|  | 	std::shared_ptr<ISharedImage> loadImageFromFileUncached(const ImageLocator & locator); | ||||||
|  | 	std::shared_ptr<ISharedImage> loadImageFromFile(const ImageLocator & locator); | ||||||
|  |  | ||||||
|  | 	std::shared_ptr<ISharedImage> transformImage(const ImageLocator & locator, std::shared_ptr<ISharedImage> image); | ||||||
|  | 	std::shared_ptr<ISharedImage> scaleImage(const ImageLocator & locator, std::shared_ptr<ISharedImage> image); | ||||||
|  |  | ||||||
|  | 	ImageLocator getLocatorForAnimationFrame(const AnimationPath & path, int frame, int group); | ||||||
|  |  | ||||||
| 	int getScalingFactor() const; | 	int getScalingFactor() const; | ||||||
|  |  | ||||||
| 	std::shared_ptr<ISharedImage> createScaledImage(std::shared_ptr<SDLImageShared> input); | 	std::shared_ptr<IImage> createImageReference(const ImageLocator & locator, std::shared_ptr<ISharedImage> input, EImageBlitMode mode); | ||||||
| public: | public: | ||||||
|  |  | ||||||
| 	// IRenderHandler implementation | 	// IRenderHandler implementation | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ | |||||||
| #include "SDL_Extensions.h" | #include "SDL_Extensions.h" | ||||||
|  |  | ||||||
| #include "../render/ColorFilter.h" | #include "../render/ColorFilter.h" | ||||||
|  | #include "../render/Colors.h" | ||||||
| #include "../render/CBitmapHandler.h" | #include "../render/CBitmapHandler.h" | ||||||
| #include "../render/CDefFile.h" | #include "../render/CDefFile.h" | ||||||
| #include "../render/Graphics.h" | #include "../render/Graphics.h" | ||||||
| @@ -22,6 +23,59 @@ | |||||||
|  |  | ||||||
| class SDLImageLoader; | class SDLImageLoader; | ||||||
|  |  | ||||||
|  | //First 8 colors in def palette used for transparency | ||||||
|  | static const SDL_Color sourcePalette[8] = { | ||||||
|  | 	{0,   255, 255, SDL_ALPHA_OPAQUE}, | ||||||
|  | 	{255, 150, 255, SDL_ALPHA_OPAQUE}, | ||||||
|  | 	{255, 100, 255, SDL_ALPHA_OPAQUE}, | ||||||
|  | 	{255, 50,  255, SDL_ALPHA_OPAQUE}, | ||||||
|  | 	{255, 0,   255, SDL_ALPHA_OPAQUE}, | ||||||
|  | 	{255, 255, 0,   SDL_ALPHA_OPAQUE}, | ||||||
|  | 	{180, 0,   255, SDL_ALPHA_OPAQUE}, | ||||||
|  | 	{0,   255, 0,   SDL_ALPHA_OPAQUE} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static const ColorRGBA targetPalette[8] = { | ||||||
|  | 	{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), | ||||||
|  | 		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; | ||||||
| @@ -83,7 +137,7 @@ SDLImageShared::SDLImageShared(const ImagePath & filename) | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| void SDLImageShared::draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, 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) | ||||||
| 		return; | 		return; | ||||||
| @@ -109,6 +163,7 @@ void SDLImageShared::draw(SDL_Surface * where, SDL_Palette * palette, const Poin | |||||||
|  |  | ||||||
| 	destShift += dest; | 	destShift += dest; | ||||||
|  |  | ||||||
|  | 	SDL_SetSurfaceColorMod(surf, colorMultiplier.r, colorMultiplier.g, colorMultiplier.b); | ||||||
| 	SDL_SetSurfaceAlphaMod(surf, alpha); | 	SDL_SetSurfaceAlphaMod(surf, alpha); | ||||||
|  |  | ||||||
| 	if (alpha != SDL_ALPHA_OPAQUE || (mode != EImageBlitMode::OPAQUE && surf->format->Amask != 0)) | 	if (alpha != SDL_ALPHA_OPAQUE || (mode != EImageBlitMode::OPAQUE && surf->format->Amask != 0)) | ||||||
| @@ -127,21 +182,19 @@ void SDLImageShared::draw(SDL_Surface * where, SDL_Palette * palette, const Poin | |||||||
| 	{ | 	{ | ||||||
| 		CSDL_Ext::blitSurface(surf, sourceRect, where, destShift); | 		CSDL_Ext::blitSurface(surf, sourceRect, where, destShift); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if (surf->format->palette) | ||||||
|  | 		SDL_SetSurfacePalette(surf, originalPalette); | ||||||
| } | } | ||||||
|  |  | ||||||
| const SDL_Palette * SDLImageShared::getPalette() const | std::shared_ptr<ISharedImage> SDLImageShared::scaleFast(const Point & size, SDL_Palette * palette) const | ||||||
| { |  | ||||||
| 	if (originalPalette == nullptr) |  | ||||||
| 		throw std::runtime_error("Palette not found!"); |  | ||||||
|  |  | ||||||
| 	return originalPalette; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| std::shared_ptr<SDLImageShared> SDLImageShared::scaleFast(const Point & size) const |  | ||||||
| { | { | ||||||
| 	float scaleX = float(size.x) / dimensions().x; | 	float scaleX = float(size.x) / dimensions().x; | ||||||
| 	float scaleY = float(size.y) / dimensions().y; | 	float scaleY = float(size.y) / dimensions().y; | ||||||
|  |  | ||||||
|  | 	if (palette && surf->format->palette) | ||||||
|  | 		SDL_SetSurfacePalette(surf, palette); | ||||||
|  |  | ||||||
| 	auto scaled = CSDL_Ext::scaleSurface(surf, (int)(surf->w * scaleX), (int)(surf->h * scaleY)); | 	auto scaled = CSDL_Ext::scaleSurface(surf, (int)(surf->w * scaleX), (int)(surf->h * scaleY)); | ||||||
|  |  | ||||||
| 	if (scaled->format && scaled->format->palette) // fix color keying, because SDL loses it at this point | 	if (scaled->format && scaled->format->palette) // fix color keying, because SDL loses it at this point | ||||||
| @@ -162,6 +215,9 @@ std::shared_ptr<SDLImageShared> SDLImageShared::scaleFast(const Point & size) co | |||||||
| 	// erase our own reference | 	// erase our own reference | ||||||
| 	SDL_FreeSurface(scaled); | 	SDL_FreeSurface(scaled); | ||||||
|  |  | ||||||
|  | 	if (surf->format->palette) | ||||||
|  | 		SDL_SetSurfacePalette(surf, originalPalette); | ||||||
|  |  | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -175,12 +231,6 @@ void SDLImageIndexed::playerColored(PlayerColor player) | |||||||
| 	graphics->setPlayerPalette(currentPalette, player); | 	graphics->setPlayerPalette(currentPalette, player); | ||||||
| } | } | ||||||
|  |  | ||||||
| void SDLImageIndexed::setFlagColor(PlayerColor player) |  | ||||||
| { |  | ||||||
| 	if(player.isValidPlayer() || player==PlayerColor::NEUTRAL) |  | ||||||
| 		graphics->setPlayerFlagColor(currentPalette, player); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool SDLImageShared::isTransparent(const Point & coords) const | bool SDLImageShared::isTransparent(const Point & coords) const | ||||||
| { | { | ||||||
| 	if (surf) | 	if (surf) | ||||||
| @@ -197,7 +247,7 @@ Point SDLImageShared::dimensions() const | |||||||
| std::shared_ptr<IImage> SDLImageShared::createImageReference(EImageBlitMode mode) | std::shared_ptr<IImage> SDLImageShared::createImageReference(EImageBlitMode mode) | ||||||
| { | { | ||||||
| 	if (surf && surf->format->palette) | 	if (surf && surf->format->palette) | ||||||
| 		return std::make_shared<SDLImageIndexed>(shared_from_this(), mode); | 		return std::make_shared<SDLImageIndexed>(shared_from_this(), originalPalette, mode); | ||||||
| 	else | 	else | ||||||
| 		return std::make_shared<SDLImageRGB>(shared_from_this(), mode); | 		return std::make_shared<SDLImageRGB>(shared_from_this(), mode); | ||||||
| } | } | ||||||
| @@ -241,8 +291,6 @@ void SDLImageShared::savePalette() | |||||||
|  |  | ||||||
| void SDLImageIndexed::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) | void SDLImageIndexed::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) | ||||||
| { | { | ||||||
| 	const SDL_Palette * originalPalette = image->getPalette(); |  | ||||||
|  |  | ||||||
| 	std::vector<SDL_Color> shifterColors(colorsToMove); | 	std::vector<SDL_Color> shifterColors(colorsToMove); | ||||||
|  |  | ||||||
| 	for(uint32_t i=0; i<colorsToMove; ++i) | 	for(uint32_t i=0; i<colorsToMove; ++i) | ||||||
| @@ -253,11 +301,12 @@ void SDLImageIndexed::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, | |||||||
|  |  | ||||||
| void SDLImageIndexed::adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) | void SDLImageIndexed::adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) | ||||||
| { | { | ||||||
| 	const SDL_Palette * originalPalette = image->getPalette(); |  | ||||||
|  |  | ||||||
| 	// Note: here we skip first colors in the palette that are predefined in H3 images | 	// Note: here we skip first colors in the palette that are predefined in H3 images | ||||||
| 	for(int i = 0; i < currentPalette->ncolors; i++) | 	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) | 		if(i < std::numeric_limits<uint32_t>::digits && ((colorsToSkipMask >> i) & 1) == 1) | ||||||
| 			continue; | 			continue; | ||||||
|  |  | ||||||
| @@ -265,13 +314,16 @@ void SDLImageIndexed::adjustPalette(const ColorFilter & shifter, uint32_t colors | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| SDLImageIndexed::SDLImageIndexed(const std::shared_ptr<SDLImageShared> & image, EImageBlitMode mode) | SDLImageIndexed::SDLImageIndexed(const std::shared_ptr<ISharedImage> & image, SDL_Palette * originalPalette, EImageBlitMode mode) | ||||||
| 	:SDLImageBase::SDLImageBase(image, mode) | 	:SDLImageBase::SDLImageBase(image, mode) | ||||||
|  | 	,originalPalette(originalPalette) | ||||||
| { | { | ||||||
| 	auto originalPalette = image->getPalette(); |  | ||||||
|  |  | ||||||
| 	currentPalette = SDL_AllocPalette(originalPalette->ncolors); | 	currentPalette = SDL_AllocPalette(originalPalette->ncolors); | ||||||
| 	SDL_SetPaletteColors(currentPalette, originalPalette->colors, 0, originalPalette->ncolors); | 	SDL_SetPaletteColors(currentPalette, originalPalette->colors, 0, originalPalette->ncolors); | ||||||
|  |  | ||||||
|  | 	setOverlayColor(Colors::TRANSPARENCY); | ||||||
|  | 	setShadowTransparency(0); | ||||||
| } | } | ||||||
|  |  | ||||||
| SDLImageIndexed::~SDLImageIndexed() | SDLImageIndexed::~SDLImageIndexed() | ||||||
| @@ -279,42 +331,96 @@ SDLImageIndexed::~SDLImageIndexed() | |||||||
| 	SDL_FreePalette(currentPalette); | 	SDL_FreePalette(currentPalette); | ||||||
| } | } | ||||||
|  |  | ||||||
| void SDLImageIndexed::setSpecialPalette(const IImage::SpecialPalette & specialPalette, uint32_t colorsToSkipMask) | void SDLImageIndexed::setShadowTransparency(float factor) | ||||||
| { | { | ||||||
| 	size_t last = std::min<size_t>(specialPalette.size(), currentPalette->ncolors); | 	ColorRGBA shadow50(0, 0, 0, 128 * factor); | ||||||
|  | 	ColorRGBA shadow25(0, 0, 0,  64 * factor); | ||||||
|  |  | ||||||
| 	for (size_t i = 0; i < last; ++i) | 	// seems to be used unconditionally | ||||||
|  | 	currentPalette->colors[1] = CSDL_Ext::toSDL(shadow25); | ||||||
|  | 	currentPalette->colors[4] = CSDL_Ext::toSDL(shadow50); | ||||||
|  |  | ||||||
|  | 	// seems to be used only if color matches | ||||||
|  | 	if (colorsSimilar(originalPalette->colors[2], sourcePalette[2])) | ||||||
|  | 		currentPalette->colors[2] = CSDL_Ext::toSDL(shadow25); | ||||||
|  |  | ||||||
|  | 	if (colorsSimilar(originalPalette->colors[3], sourcePalette[3])) | ||||||
|  | 		currentPalette->colors[3] = CSDL_Ext::toSDL(shadow50); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void SDLImageIndexed::setOverlayColor(const ColorRGBA & color) | ||||||
|  | { | ||||||
|  | 	for (int i : {5,6,7}) | ||||||
| 	{ | 	{ | ||||||
| 		if(i < std::numeric_limits<uint32_t>::digits && ((colorsToSkipMask >> i) & 1) == 1) | 		if (colorsSimilar(originalPalette->colors[i], sourcePalette[i])) | ||||||
| 			currentPalette->colors[i] = CSDL_Ext::toSDL(specialPalette[i]); | 			currentPalette->colors[i] = CSDL_Ext::toSDL(addColors(targetPalette[i], color)); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void SDLImageIndexed::setShadowEnabled(bool on) | ||||||
|  | { | ||||||
|  | 	if (on) | ||||||
|  | 		setShadowTransparency(1.0); | ||||||
|  | 	else | ||||||
|  | 		setShadowTransparency(0); | ||||||
|  |  | ||||||
|  | 	shadowEnabled = on; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void SDLImageIndexed::setBodyEnabled(bool on) | ||||||
|  | { | ||||||
|  | 	if (on) | ||||||
|  | 		adjustPalette(ColorFilter::genEmptyShifter(), 0); | ||||||
|  | 	else | ||||||
|  | 		adjustPalette(ColorFilter::genAlphaShifter(0), 0); | ||||||
|  |  | ||||||
|  | 	bodyEnabled = on; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void SDLImageIndexed::setOverlayEnabled(bool on) | ||||||
|  | { | ||||||
|  | 	if (on) | ||||||
|  | 		setOverlayColor(Colors::WHITE_TRUE); | ||||||
|  | 	else | ||||||
|  | 		setOverlayColor(Colors::TRANSPARENCY); | ||||||
|  | 	overlayEnabled = on; | ||||||
|  | } | ||||||
|  |  | ||||||
| SDLImageShared::~SDLImageShared() | SDLImageShared::~SDLImageShared() | ||||||
| { | { | ||||||
| 	SDL_FreeSurface(surf); | 	SDL_FreeSurface(surf); | ||||||
| 	SDL_FreePalette(originalPalette); | 	SDL_FreePalette(originalPalette); | ||||||
| } | } | ||||||
|  |  | ||||||
| SDLImageBase::SDLImageBase(const std::shared_ptr<SDLImageShared> & image, EImageBlitMode mode) | SDLImageBase::SDLImageBase(const std::shared_ptr<ISharedImage> & image, EImageBlitMode mode) | ||||||
| 	:image(image) | 	:image(image) | ||||||
| 	, alphaValue(SDL_ALPHA_OPAQUE) | 	, alphaValue(SDL_ALPHA_OPAQUE) | ||||||
| 	, blitMode(mode) | 	, blitMode(mode) | ||||||
| {} | {} | ||||||
|  |  | ||||||
|  | std::shared_ptr<ISharedImage> SDLImageBase::getSharedImage() const | ||||||
|  | { | ||||||
|  | 	return image; | ||||||
|  | } | ||||||
|  |  | ||||||
| void SDLImageRGB::draw(SDL_Surface * where, const Point & pos, const Rect * src) const | void SDLImageRGB::draw(SDL_Surface * where, const Point & pos, const Rect * src) const | ||||||
| { | { | ||||||
| 	image->draw(where, nullptr, pos, src, alphaValue, blitMode); | 	image->draw(where, nullptr, pos, src, Colors::WHITE_TRUE, alphaValue, blitMode); | ||||||
| } | } | ||||||
|  |  | ||||||
| void SDLImageIndexed::draw(SDL_Surface * where, const Point & pos, const Rect * src) const | void SDLImageIndexed::draw(SDL_Surface * where, const Point & pos, const Rect * src) const | ||||||
| { | { | ||||||
| 	image->draw(where, currentPalette, pos, src, alphaValue, blitMode); | 	image->draw(where, currentPalette, pos, src, Colors::WHITE_TRUE, alphaValue, blitMode); | ||||||
| } | } | ||||||
|  |  | ||||||
| void SDLImageBase::scaleFast(const Point & size) | void SDLImageIndexed::scaleFast(const Point & size) | ||||||
| { | { | ||||||
| 	image = image->scaleFast(size); | 	image = image->scaleFast(size, currentPalette); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void SDLImageRGB::scaleFast(const Point & size) | ||||||
|  | { | ||||||
|  | 	image = image->scaleFast(size, nullptr); | ||||||
| } | } | ||||||
|  |  | ||||||
| void SDLImageBase::exportBitmap(const boost::filesystem::path & path) const | void SDLImageBase::exportBitmap(const boost::filesystem::path & path) const | ||||||
| @@ -342,15 +448,27 @@ void SDLImageBase::setBlitMode(EImageBlitMode mode) | |||||||
| 	blitMode = mode; | 	blitMode = mode; | ||||||
| } | } | ||||||
|  |  | ||||||
| void SDLImageRGB::setSpecialPalette(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) | void SDLImageRGB::setShadowEnabled(bool on) | ||||||
|  | { | ||||||
|  | 	// Not supported. Theoretically we can try to extract all pixels of specific colors, but better to use 8-bit images or composite images | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void SDLImageRGB::setBodyEnabled(bool on) | ||||||
|  | { | ||||||
|  | 	// Not supported. Theoretically we can try to extract all pixels of specific colors, but better to use 8-bit images or composite images | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void SDLImageRGB::setOverlayEnabled(bool on) | ||||||
|  | { | ||||||
|  | 	// Not supported. Theoretically we can try to extract all pixels of specific colors, but better to use 8-bit images or composite images | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void SDLImageRGB::setOverlayColor(const ColorRGBA & color) | ||||||
| {} | {} | ||||||
|  |  | ||||||
| void SDLImageRGB::playerColored(PlayerColor player) | void SDLImageRGB::playerColored(PlayerColor player) | ||||||
| {} | {} | ||||||
|  |  | ||||||
| void SDLImageRGB::setFlagColor(PlayerColor player) |  | ||||||
| {} |  | ||||||
|  |  | ||||||
| void SDLImageRGB::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) | void SDLImageRGB::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) | ||||||
| {} | {} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -47,7 +47,7 @@ public: | |||||||
| 	SDLImageShared(SDL_Surface * from); | 	SDLImageShared(SDL_Surface * from); | ||||||
| 	~SDLImageShared(); | 	~SDLImageShared(); | ||||||
|  |  | ||||||
| 	void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, 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; | ||||||
|  |  | ||||||
| 	void exportBitmap(const boost::filesystem::path & path) const override; | 	void exportBitmap(const boost::filesystem::path & path) const override; | ||||||
| 	Point dimensions() const override; | 	Point dimensions() const override; | ||||||
| @@ -55,9 +55,7 @@ public: | |||||||
| 	std::shared_ptr<IImage> createImageReference(EImageBlitMode mode) override; | 	std::shared_ptr<IImage> createImageReference(EImageBlitMode mode) override; | ||||||
| 	std::shared_ptr<ISharedImage> horizontalFlip() const override; | 	std::shared_ptr<ISharedImage> horizontalFlip() const override; | ||||||
| 	std::shared_ptr<ISharedImage> verticalFlip() const override; | 	std::shared_ptr<ISharedImage> verticalFlip() const override; | ||||||
| 	std::shared_ptr<SDLImageShared> scaleFast(const Point & size) const; | 	std::shared_ptr<ISharedImage> scaleFast(const Point & size, SDL_Palette * palette) const override; | ||||||
|  |  | ||||||
| 	const SDL_Palette * getPalette() const; |  | ||||||
|  |  | ||||||
| 	friend class SDLImageLoader; | 	friend class SDLImageLoader; | ||||||
| }; | }; | ||||||
| @@ -65,36 +63,46 @@ public: | |||||||
| class SDLImageBase : public IImage, boost::noncopyable | class SDLImageBase : public IImage, boost::noncopyable | ||||||
| { | { | ||||||
| protected: | protected: | ||||||
| 	std::shared_ptr<SDLImageShared> image; | 	std::shared_ptr<ISharedImage> image; | ||||||
|  |  | ||||||
| 	uint8_t alphaValue; | 	uint8_t alphaValue; | ||||||
| 	EImageBlitMode blitMode; | 	EImageBlitMode blitMode; | ||||||
|  |  | ||||||
| public: | public: | ||||||
| 	SDLImageBase(const std::shared_ptr<SDLImageShared> & image, EImageBlitMode mode); | 	SDLImageBase(const std::shared_ptr<ISharedImage> & image, EImageBlitMode mode); | ||||||
|  |  | ||||||
| 	void scaleFast(const Point & size) override; |  | ||||||
| 	void exportBitmap(const boost::filesystem::path & path) const override; | 	void exportBitmap(const boost::filesystem::path & path) const override; | ||||||
| 	bool isTransparent(const Point & coords) const override; | 	bool isTransparent(const Point & coords) const override; | ||||||
| 	Point dimensions() const override; | 	Point dimensions() const override; | ||||||
| 	void setAlpha(uint8_t value) override; | 	void setAlpha(uint8_t value) override; | ||||||
| 	void setBlitMode(EImageBlitMode mode) override; | 	void setBlitMode(EImageBlitMode mode) override; | ||||||
|  | 	std::shared_ptr<ISharedImage> getSharedImage() const override; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class SDLImageIndexed final : public SDLImageBase | class SDLImageIndexed final : public SDLImageBase | ||||||
| { | { | ||||||
| 	SDL_Palette * currentPalette = nullptr; | 	SDL_Palette * currentPalette = nullptr; | ||||||
|  | 	SDL_Palette * originalPalette = nullptr; | ||||||
|  |  | ||||||
|  | 	bool bodyEnabled = true; | ||||||
|  | 	bool shadowEnabled = false; | ||||||
|  | 	bool overlayEnabled = false; | ||||||
|  |  | ||||||
|  | 	void setShadowTransparency(float factor); | ||||||
| public: | public: | ||||||
| 	SDLImageIndexed(const std::shared_ptr<SDLImageShared> & image, EImageBlitMode mode); | 	SDLImageIndexed(const std::shared_ptr<ISharedImage> & image, SDL_Palette * palette, EImageBlitMode mode); | ||||||
| 	~SDLImageIndexed(); | 	~SDLImageIndexed(); | ||||||
|  |  | ||||||
| 	void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override; | 	void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override; | ||||||
| 	void setSpecialPalette(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) override; | 	void setOverlayColor(const ColorRGBA & color) override; | ||||||
| 	void playerColored(PlayerColor player) override; | 	void playerColored(PlayerColor player) override; | ||||||
| 	void setFlagColor(PlayerColor player) override; |  | ||||||
| 	void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; | 	void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; | ||||||
| 	void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; | 	void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; | ||||||
|  | 	void scaleFast(const Point & size) override; | ||||||
|  |  | ||||||
|  | 	void setShadowEnabled(bool on) override; | ||||||
|  | 	void setBodyEnabled(bool on) override; | ||||||
|  | 	void setOverlayEnabled(bool on) override; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| class SDLImageRGB final : public SDLImageBase | class SDLImageRGB final : public SDLImageBase | ||||||
| @@ -103,9 +111,13 @@ public: | |||||||
| 	using SDLImageBase::SDLImageBase; | 	using SDLImageBase::SDLImageBase; | ||||||
|  |  | ||||||
| 	void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override; | 	void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override; | ||||||
| 	void setSpecialPalette(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) override; | 	void setOverlayColor(const ColorRGBA & color) override; | ||||||
| 	void playerColored(PlayerColor player) override; | 	void playerColored(PlayerColor player) override; | ||||||
| 	void setFlagColor(PlayerColor player) override; |  | ||||||
| 	void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; | 	void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; | ||||||
| 	void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; | 	void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; | ||||||
|  | 	void scaleFast(const Point & size) override; | ||||||
|  |  | ||||||
|  | 	void setShadowEnabled(bool on) override; | ||||||
|  | 	void setBodyEnabled(bool on) override; | ||||||
|  | 	void setOverlayEnabled(bool on) override; | ||||||
| }; | }; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user