瀏覽代碼

Created separate classes for shared, unchangeable image and image
manipulators owned by UI elements

Ivan Savenko 1 年之前
父節點
當前提交
47de9a62dc

+ 3 - 3
client/CPlayerInterface.cpp

@@ -1143,9 +1143,9 @@ void CPlayerInterface::showMapObjectSelectDialog(QueryID askID, const Component
 		const CGTownInstance * t = dynamic_cast<const CGTownInstance *>(cb->getObj(obj));
 		if(t)
 		{
-			std::shared_ptr<CAnimation> a = GH.renderHandler().loadAnimation(AnimationPath::builtin("ITPA"));
-			a->preload();
-			images.push_back(a->getImage(t->town->clientInfo.icons[t->hasFort()][false] + 2)->scaleFast(Point(35, 23)));
+			auto image = GH.renderHandler().loadImage(AnimationPath::builtin("ITPA"), t->town->clientInfo.icons[t->hasFort()][false] + 2, 0);
+			image->scaleFast(Point(35, 23));
+			images.push_back(image);
 		}
 	}
 

+ 3 - 3
client/render/Canvas.cpp

@@ -81,14 +81,14 @@ void Canvas::draw(const std::shared_ptr<IImage>& image, const Point & pos)
 {
 	assert(image);
 	if (image)
-		image->draw(surface, renderArea.x + pos.x, renderArea.y + pos.y);
+		image->draw(surface, pos + renderArea.topLeft());
 }
 
 void Canvas::draw(const std::shared_ptr<IImage>& image, const Point & pos, const Rect & sourceRect)
 {
 	assert(image);
 	if (image)
-		image->draw(surface, renderArea.x + pos.x, renderArea.y + pos.y, &sourceRect);
+		image->draw(surface, pos + renderArea.topLeft(), &sourceRect);
 }
 
 void Canvas::draw(const Canvas & image, const Point & pos)
@@ -192,7 +192,7 @@ void Canvas::fillTexture(const std::shared_ptr<IImage>& image)
 	for (int y=0; y < surface->h; y+= imageArea.h)
 	{
 		for (int x=0; x < surface->w; x+= imageArea.w)
-			image->draw(surface, renderArea.x + x, renderArea.y + y);
+			image->draw(surface, Point(renderArea.x + x, renderArea.y + y));
 	}
 }
 

+ 21 - 58
client/render/Graphics.cpp

@@ -135,71 +135,34 @@ Graphics::Graphics()
 	//(!) do not load any CAnimation here
 }
 
-void Graphics::blueToPlayersAdv(SDL_Surface * sur, PlayerColor player)
+void Graphics::setPlayerPalette(SDL_Palette * targetPalette, PlayerColor player)
 {
-	if(sur->format->palette)
+	SDL_Color palette[32];
+	if(player.isValidPlayer())
 	{
-		SDL_Color palette[32];
-		if(player.isValidPlayer())
-		{
-			for(int i=0; i<32; ++i)
-				palette[i] = CSDL_Ext::toSDL(playerColorPalette[player][i]);
-		}
-		else if(player == PlayerColor::NEUTRAL)
-		{
-			for(int i=0; i<32; ++i)
-				palette[i] = CSDL_Ext::toSDL(neutralColorPalette[i]);
-		}
-		else
-		{
-			logGlobal->error("Wrong player id in blueToPlayersAdv (%s)!", player.toString());
-			return;
-		}
-//FIXME: not all player colored images have player palette at last 32 indexes
-//NOTE: following code is much more correct but still not perfect (bugged with status bar)
-		CSDL_Ext::setColors(sur, palette, 224, 32);
-
-
-#if 0
-
-		SDL_Color * bluePalette = playerColorPalette + 32;
-
-		SDL_Palette * oldPalette = sur->format->palette;
-
-		SDL_Palette * newPalette = SDL_AllocPalette(256);
-
-		for(size_t destIndex = 0; destIndex < 256; destIndex++)
-		{
-			SDL_Color old = oldPalette->colors[destIndex];
-
-			bool found = false;
-
-			for(size_t srcIndex = 0; srcIndex < 32; srcIndex++)
-			{
-				if(old.b == bluePalette[srcIndex].b && old.g == bluePalette[srcIndex].g && old.r == bluePalette[srcIndex].r)
-				{
-					found = true;
-					newPalette->colors[destIndex] = palette[srcIndex];
-					break;
-				}
-			}
-			if(!found)
-				newPalette->colors[destIndex] = old;
-		}
-
-		SDL_SetSurfacePalette(sur, newPalette);
+		for(int i=0; i<32; ++i)
+			palette[i] = CSDL_Ext::toSDL(playerColorPalette[player][i]);
+	}
+	else
+	{
+		for(int i=0; i<32; ++i)
+			palette[i] = CSDL_Ext::toSDL(neutralColorPalette[i]);
+	}
 
-		SDL_FreePalette(newPalette);
+	SDL_SetPaletteColors(targetPalette, palette, 224, 32);
+}
 
-#endif // 0
+void Graphics::setPlayerFlagColor(SDL_Palette * targetPalette, PlayerColor player)
+{
+	if(player.isValidPlayer())
+	{
+		SDL_Color color = CSDL_Ext::toSDL(playerColors[player.getNum()]);
+		SDL_SetPaletteColors(targetPalette, &color, 5, 1);
 	}
 	else
 	{
-		//TODO: implement. H3 method works only for images with palettes.
-		// Add some kind of player-colored overlay?
-		// Or keep palette approach here and replace only colors of specific value(s)
-		// Or just wait for OpenGL support?
-		logGlobal->warn("Image must have palette to be player-colored!");
+		SDL_Color color = CSDL_Ext::toSDL(neutralColor);
+		SDL_SetPaletteColors(targetPalette, &color, 5, 1);
 	}
 }
 

+ 3 - 2
client/render/Graphics.h

@@ -27,7 +27,7 @@ class JsonNode;
 
 VCMI_LIB_NAMESPACE_END
 
-struct SDL_Surface;
+struct SDL_Palette;
 class IFont;
 
 /// Handles fonts, hero images, town images, various graphics
@@ -60,7 +60,8 @@ public:
 	//functions
 	Graphics();
 
-	void blueToPlayersAdv(SDL_Surface * sur, PlayerColor player); //replaces blue interface colour with a color of player
+	void setPlayerPalette(SDL_Palette * sur, PlayerColor player);
+	void setPlayerFlagColor(SDL_Palette * sur, PlayerColor player);
 };
 
 extern Graphics * graphics;

+ 15 - 3
client/render/IImage.h

@@ -46,10 +46,10 @@ public:
 	static constexpr int32_t SPECIAL_PALETTE_MASK_CREATURES = 0b11110011;
 
 	//draws image on surface "where" at position
-	virtual void draw(SDL_Surface * where, int posX = 0, int posY = 0, const Rect * src = nullptr) const = 0;
-	virtual void draw(SDL_Surface * where, const Rect * dest, const Rect * src) const = 0;
+	virtual void draw(SDL_Surface * where, const Point & pos, const Rect * src = nullptr) const = 0;
+	//virtual void draw(SDL_Surface * where, const Rect * dest, const Rect * src) const = 0;
 
-	virtual std::shared_ptr<IImage> scaleFast(const Point & size) const = 0;
+	virtual void scaleFast(const Point & size) = 0;
 
 	virtual void exportBitmap(const boost::filesystem::path & path) const = 0;
 
@@ -81,3 +81,15 @@ public:
 
 	virtual ~IImage() = default;
 };
+
+class IConstImage
+{
+public:
+	virtual Point dimensions() const = 0;
+	virtual void exportBitmap(const boost::filesystem::path & path) const = 0;
+	virtual bool isTransparent(const Point & coords) const = 0;
+
+	virtual std::shared_ptr<IImage> createImageReference() = 0;
+
+	virtual ~IConstImage() = default;
+};

+ 0 - 2
client/render/IImageLoader.h

@@ -14,8 +14,6 @@ VCMI_LIB_NAMESPACE_BEGIN
 class Point;
 VCMI_LIB_NAMESPACE_END
 
-class SDLImage;
-
 struct SDL_Color;
 
 class IImageLoader

+ 1 - 1
client/renderSDL/CursorHardware.cpp

@@ -49,7 +49,7 @@ void CursorHardware::setImage(std::shared_ptr<IImage> image, const Point & pivot
 
 	CSDL_Ext::fillSurface(cursorSurface, CSDL_Ext::toSDL(Colors::TRANSPARENCY));
 
-	image->draw(cursorSurface);
+	image->draw(cursorSurface, Point(0,0));
 
 	auto oldCursor = cursor;
 	cursor = SDL_CreateColorCursor(cursorSurface, pivotOffset.x, pivotOffset.y);

+ 1 - 1
client/renderSDL/CursorSoftware.cpp

@@ -58,7 +58,7 @@ void CursorSoftware::updateTexture()
 
 	CSDL_Ext::fillSurface(cursorSurface, CSDL_Ext::toSDL(Colors::TRANSPARENCY));
 
-	cursorImage->draw(cursorSurface);
+	cursorImage->draw(cursorSurface, Point(0,0));
 	SDL_UpdateTexture(cursorTexture, nullptr, cursorSurface->pixels, cursorSurface->pitch);
 	needUpdate = false;
 }

+ 7 - 7
client/renderSDL/RenderHandler.cpp

@@ -134,13 +134,13 @@ std::shared_ptr<IImage> RenderHandler::loadImage(const AnimationPath & path, int
 
 	auto it = animationFrames.find(locator);
 	if (it != animationFrames.end())
-		return it->second;
+		return it->second->createImageReference();
 
 	auto defFile = getAnimationFile(path);
-	auto result = std::make_shared<SDLImage>(defFile.get(), frame, group);
+	auto result = std::make_shared<SDLImageConst>(defFile.get(), frame, group);
 
 	animationFrames[locator] = result;
-	return result;
+	return result->createImageReference();
 }
 
 //std::vector<std::shared_ptr<IImage>> RenderHandler::loadImageGroup(const AnimationPath & path, int group)
@@ -165,16 +165,16 @@ std::shared_ptr<IImage> RenderHandler::loadImage(const ImagePath & path, EImageB
 {
 	auto it = imageFiles.find(path);
 	if (it != imageFiles.end())
-		return it->second;
+		return it->second->createImageReference();
 
-	auto result = std::make_shared<SDLImage>(path, mode);
+	auto result = std::make_shared<SDLImageConst>(path, mode);
 	imageFiles[path] = result;
-	return result;
+	return result->createImageReference();
 }
 
 std::shared_ptr<IImage> RenderHandler::createImage(SDL_Surface * source)
 {
-	return std::make_shared<SDLImage>(source, EImageBlitMode::ALPHA);
+	return std::make_shared<SDLImageConst>(source, EImageBlitMode::ALPHA)->createImageReference();
 }
 
 std::shared_ptr<CAnimation> RenderHandler::loadAnimation(const AnimationPath & path)

+ 3 - 2
client/renderSDL/RenderHandler.h

@@ -12,6 +12,7 @@
 #include "../render/IRenderHandler.h"
 
 class CDefFile;
+class IConstImage;
 
 class RenderHandler : public IRenderHandler
 {
@@ -35,8 +36,8 @@ class RenderHandler : public IRenderHandler
 
 	std::map<AnimationPath, std::shared_ptr<CDefFile>> animationFiles;
 	std::map<AnimationPath, AnimationLayoutMap> animationLayouts;
-	std::map<ImagePath, std::shared_ptr<IImage>> imageFiles;
-	std::map<AnimationLocator, std::shared_ptr<IImage>> animationFrames;
+	std::map<ImagePath, std::shared_ptr<IConstImage>> imageFiles;
+	std::map<AnimationLocator, std::shared_ptr<IConstImage>> animationFrames;
 
 	std::shared_ptr<CDefFile> getAnimationFile(const AnimationPath & path);
 	const AnimationLayoutMap & getAnimationLayout(const AnimationPath & path);

+ 166 - 92
client/renderSDL/SDLImage.cpp

@@ -1,4 +1,4 @@
-/*
+/*
  * SDLImage.cpp, part of VCMI engine
  *
  * Authors: listed in file AUTHORS in main folder
@@ -32,7 +32,7 @@ int IImage::height() const
 	return dimensions().y;
 }
 
-SDLImage::SDLImage(CDefFile * data, size_t frame, size_t group)
+SDLImageConst::SDLImageConst(CDefFile * data, size_t frame, size_t group)
 	: surf(nullptr),
 	margins(0, 0),
 	fullSize(0, 0),
@@ -42,10 +42,9 @@ SDLImage::SDLImage(CDefFile * data, size_t frame, size_t group)
 	data->loadFrame(frame, group, loader);
 
 	savePalette();
-	setBlitMode(EImageBlitMode::ALPHA);
 }
 
-SDLImage::SDLImage(SDL_Surface * from, EImageBlitMode mode)
+SDLImageConst::SDLImageConst(SDL_Surface * from, EImageBlitMode mode)
 	: surf(nullptr),
 	margins(0, 0),
 	fullSize(0, 0),
@@ -56,14 +55,13 @@ SDLImage::SDLImage(SDL_Surface * from, EImageBlitMode mode)
 		return;
 
 	savePalette();
-	setBlitMode(mode);
 
 	surf->refcount++;
 	fullSize.x = surf->w;
 	fullSize.y = surf->h;
 }
 
-SDLImage::SDLImage(const ImagePath & filename, EImageBlitMode mode)
+SDLImageConst::SDLImageConst(const ImagePath & filename, EImageBlitMode mode)
 	: surf(nullptr),
 	margins(0, 0),
 	fullSize(0, 0),
@@ -79,22 +77,13 @@ SDLImage::SDLImage(const ImagePath & filename, EImageBlitMode mode)
 	else
 	{
 		savePalette();
-		setBlitMode(mode);
 		fullSize.x = surf->w;
 		fullSize.y = surf->h;
 	}
 }
 
-void SDLImage::draw(SDL_Surface *where, int posX, int posY, const Rect *src) const
-{
-	if(!surf)
-		return;
-
-	Rect destRect(posX, posY, surf->w, surf->h);
-	draw(where, &destRect, src);
-}
 
-void SDLImage::draw(SDL_Surface* where, const Rect * dest, const Rect* src) const
+void SDLImageConst::draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, uint8_t alpha, EImageBlitMode mode) const
 {
 	if (!surf)
 		return;
@@ -118,14 +107,19 @@ void SDLImage::draw(SDL_Surface* where, const Rect * dest, const Rect* src) cons
 	else
 		destShift = margins;
 
-	if(dest)
-		destShift += dest->topLeft();
+	destShift += dest;
+
+	SDL_SetSurfaceAlphaMod(surf, alpha);
+
+	if (mode == EImageBlitMode::OPAQUE)
+		SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_NONE);
+	else
+		SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_BLEND);
 
-	uint8_t perSurfaceAlpha;
-	if (SDL_GetSurfaceAlphaMod(surf, &perSurfaceAlpha) != 0)
-		logGlobal->error("SDL_GetSurfaceAlphaMod faied! %s", SDL_GetError());
+	if (palette && surf->format->palette)
+		SDL_SetSurfacePalette(surf, palette);
 
-	if(surf->format->BitsPerPixel == 8 && perSurfaceAlpha == SDL_ALPHA_OPAQUE && blitMode == EImageBlitMode::ALPHA)
+	if(surf->format->BitsPerPixel == 8 && alpha == SDL_ALPHA_OPAQUE && mode == EImageBlitMode::ALPHA)
 	{
 		CSDL_Ext::blit8bppAlphaTo24bpp(surf, sourceRect, where, destShift);
 	}
@@ -135,10 +129,18 @@ void SDLImage::draw(SDL_Surface* where, const Rect * dest, const Rect* src) cons
 	}
 }
 
-std::shared_ptr<IImage> SDLImage::scaleFast(const Point & size) const
+const SDL_Palette * SDLImageConst::getPalette() const
 {
-	float scaleX = float(size.x) / width();
-	float scaleY = float(size.y) / height();
+	if (originalPalette == nullptr)
+		throw std::runtime_error("Palette not found!");
+
+	return originalPalette;
+}
+
+std::shared_ptr<SDLImageConst> SDLImageConst::scaleFast(const Point & size) const
+{
+	float scaleX = float(size.x) / dimensions().x;
+	float scaleY = float(size.y) / dimensions().y;
 
 	auto scaled = CSDL_Ext::scaleSurfaceFast(surf, (int)(surf->w * scaleX), (int)(surf->h * scaleY));
 
@@ -149,7 +151,7 @@ std::shared_ptr<IImage> SDLImage::scaleFast(const Point & size) const
 	else
 		CSDL_Ext::setDefaultColorKey(scaled);//just in case
 
-	auto * ret = new SDLImage(scaled, EImageBlitMode::ALPHA);
+	auto ret = std::make_shared<SDLImageConst>(scaled, EImageBlitMode::ALPHA);
 
 	ret->fullSize.x = (int) round((float)fullSize.x * scaleX);
 	ret->fullSize.y = (int) round((float)fullSize.y * scaleY);
@@ -160,45 +162,26 @@ std::shared_ptr<IImage> SDLImage::scaleFast(const Point & size) const
 	// erase our own reference
 	SDL_FreeSurface(scaled);
 
-	return std::shared_ptr<IImage>(ret);
+	return ret;
 }
 
-void SDLImage::exportBitmap(const boost::filesystem::path& path) const
+void SDLImageConst::exportBitmap(const boost::filesystem::path& path) const
 {
 	SDL_SaveBMP(surf, path.string().c_str());
 }
 
-void SDLImage::playerColored(PlayerColor player)
-{
-	if (!surf)
-		return;
-	graphics->blueToPlayersAdv(surf, player);
-}
-
-void SDLImage::setAlpha(uint8_t value)
+void SDLImageIndexed::playerColored(PlayerColor player)
 {
-	CSDL_Ext::setAlpha (surf, value);
-	if (value != 255)
-		SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_BLEND);
-}
-
-void SDLImage::setBlitMode(EImageBlitMode mode)
-{
-	blitMode = mode;
-
-	if (blitMode != EImageBlitMode::OPAQUE && surf->format->Amask != 0)
-		SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_BLEND);
-	else
-		SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_NONE);
+	graphics->setPlayerPalette(currentPalette, player);
 }
 
-void SDLImage::setFlagColor(PlayerColor player)
+void SDLImageIndexed::setFlagColor(PlayerColor player)
 {
 	if(player.isValidPlayer() || player==PlayerColor::NEUTRAL)
-		CSDL_Ext::setPlayerColor(surf, player);
+		graphics->setPlayerFlagColor(currentPalette, player);
 }
 
-bool SDLImage::isTransparent(const Point & coords) const
+bool SDLImageConst::isTransparent(const Point & coords) const
 {
 	if (surf)
 		return CSDL_Ext::isTransparent(surf, coords.x, coords.y);
@@ -206,33 +189,45 @@ bool SDLImage::isTransparent(const Point & coords) const
 		return true;
 }
 
-Point SDLImage::dimensions() const
+Point SDLImageConst::dimensions() const
 {
 	return fullSize;
 }
 
-void SDLImage::horizontalFlip()
+std::shared_ptr<IImage> SDLImageConst::createImageReference()
 {
-	margins.y = fullSize.y - surf->h - margins.y;
+	if (surf->format->palette)
+		return std::make_shared<SDLImageIndexed>(shared_from_this());
+	else
+		return std::make_shared<SDLImageRGB>(shared_from_this());
+}
 
-	//todo: modify in-place
+std::shared_ptr<SDLImageConst> SDLImageConst::horizontalFlip() const
+{
 	SDL_Surface * flipped = CSDL_Ext::horizontalFlip(surf);
-	SDL_FreeSurface(surf);
-	surf = flipped;
+	auto ret = std::make_shared<SDLImageConst>(flipped, EImageBlitMode::ALPHA);
+	ret->fullSize = fullSize;
+	ret->margins.x = margins.x;
+	ret->margins.y = fullSize.y - surf->h - margins.y;
+	ret->fullSize = fullSize;
+
+	return ret;
 }
 
-void SDLImage::verticalFlip()
+std::shared_ptr<SDLImageConst> SDLImageConst::verticalFlip() const
 {
-	margins.x = fullSize.x - surf->w - margins.x;
-
-	//todo: modify in-place
 	SDL_Surface * flipped = CSDL_Ext::verticalFlip(surf);
-	SDL_FreeSurface(surf);
-	surf = flipped;
+	auto ret = std::make_shared<SDLImageConst>(flipped, EImageBlitMode::ALPHA);
+	ret->fullSize = fullSize;
+	ret->margins.x = fullSize.x - surf->w - margins.x;
+	ret->margins.y = margins.y;
+	ret->fullSize = fullSize;
+
+	return ret;
 }
 
 // Keep the original palette, in order to do color switching operation
-void SDLImage::savePalette()
+void SDLImageConst::savePalette()
 {
 	// For some images that don't have palette, skip this
 	if(surf->format->palette == nullptr)
@@ -244,53 +239,132 @@ void SDLImage::savePalette()
 	SDL_SetPaletteColors(originalPalette, surf->format->palette->colors, 0, DEFAULT_PALETTE_COLORS);
 }
 
-void SDLImage::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove)
+void SDLImageIndexed::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove)
 {
-	if(surf->format->palette)
-	{
-		std::vector<SDL_Color> shifterColors(colorsToMove);
+	const SDL_Palette * originalPalette = image->getPalette();
 
-		for(uint32_t i=0; i<colorsToMove; ++i)
-		{
-			shifterColors[(i+distanceToMove)%colorsToMove] = originalPalette->colors[firstColorID + i];
-		}
-		CSDL_Ext::setColors(surf, shifterColors.data(), firstColorID, colorsToMove);
-	}
+	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 SDLImage::adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask)
+void SDLImageIndexed::adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask)
 {
-	if(originalPalette == nullptr)
-		return;
-
-	SDL_Palette* palette = surf->format->palette;
+	const SDL_Palette * originalPalette = image->getPalette();
 
 	// Note: here we skip first colors in the palette that are predefined in H3 images
-	for(int i = 0; i < palette->ncolors; i++)
+	for(int i = 0; i < currentPalette->ncolors; i++)
 	{
 		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])));
+		currentPalette->colors[i] = CSDL_Ext::toSDL(shifter.shiftColor(CSDL_Ext::fromSDL(originalPalette->colors[i])));
 	}
 }
 
-void SDLImage::setSpecialPalette(const IImage::SpecialPalette & specialPalette, uint32_t colorsToSkipMask)
+SDLImageIndexed::SDLImageIndexed(const std::shared_ptr<SDLImageConst> & image)
+	:SDLImageBase::SDLImageBase(image)
 {
-	if(surf->format->palette)
-	{
-		size_t last = std::min<size_t>(specialPalette.size(), surf->format->palette->ncolors);
+	auto originalPalette = image->getPalette();
 
-		for (size_t i = 0; i < last; ++i)
-		{
-			if(i < std::numeric_limits<uint32_t>::digits && ((colorsToSkipMask >> i) & 1) == 1)
-				surf->format->palette->colors[i] = CSDL_Ext::toSDL(specialPalette[i]);
-		}
+	currentPalette = SDL_AllocPalette(originalPalette->ncolors);
+	SDL_SetPaletteColors(currentPalette, originalPalette->colors, 0, originalPalette->ncolors);
+}
+
+SDLImageIndexed::~SDLImageIndexed()
+{
+	SDL_FreePalette(currentPalette);
+}
+
+void SDLImageIndexed::setSpecialPallete(const IImage::SpecialPalette & specialPalette, uint32_t colorsToSkipMask)
+{
+	size_t last = std::min<size_t>(specialPalette.size(), currentPalette->ncolors);
+
+	for (size_t i = 0; i < last; ++i)
+	{
+		if(i < std::numeric_limits<uint32_t>::digits && ((colorsToSkipMask >> i) & 1) == 1)
+			currentPalette->colors[i] = CSDL_Ext::toSDL(specialPalette[i]);
 	}
 }
 
-SDLImage::~SDLImage()
+SDLImageConst::~SDLImageConst()
 {
 	SDL_FreeSurface(surf);
 	SDL_FreePalette(originalPalette);
 }
+
+SDLImageBase::SDLImageBase(const std::shared_ptr<SDLImageConst> & image)
+	:image(image)
+	, alphaValue(SDL_ALPHA_OPAQUE)
+	, blitMode(EImageBlitMode::ALPHA)
+{}
+
+void SDLImageRGB::draw(SDL_Surface * where, const Point & pos, const Rect * src) const
+{
+	image->draw(where, nullptr, pos, src, alphaValue, blitMode);
+}
+
+void SDLImageIndexed::draw(SDL_Surface * where, const Point & pos, const Rect * src) const
+{
+	image->draw(where, currentPalette, pos, src, alphaValue, blitMode);
+}
+
+void SDLImageBase::scaleFast(const Point & size)
+{
+	image = image->scaleFast(size);
+}
+
+void SDLImageBase::exportBitmap(const boost::filesystem::path & path) const
+{
+	image->exportBitmap(path);
+}
+
+bool SDLImageBase::isTransparent(const Point & coords) const
+{
+	return image->isTransparent(coords);
+}
+
+Point SDLImageBase::dimensions() const
+{
+	return image->dimensions();
+}
+
+void SDLImageBase::setAlpha(uint8_t value)
+{
+	alphaValue = value;
+}
+
+void SDLImageBase::setBlitMode(EImageBlitMode mode)
+{
+	blitMode = mode;
+}
+
+void SDLImageBase::horizontalFlip()
+{
+	image = image->horizontalFlip();
+}
+
+void SDLImageBase::verticalFlip()
+{
+	image = image->verticalFlip();
+}
+
+void SDLImageRGB::setSpecialPallete(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask)
+{}
+
+void SDLImageRGB::playerColored(PlayerColor player)
+{}
+
+void SDLImageRGB::setFlagColor(PlayerColor player)
+{}
+
+void SDLImageRGB::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove)
+{}
+
+void SDLImageRGB::adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask)
+{}
+
+

+ 59 - 21
client/renderSDL/SDLImage.h

@@ -24,9 +24,9 @@ struct SDL_Palette;
 /*
  * Wrapper around SDL_Surface
  */
-class SDLImage : public IImage
+class SDLImageConst final : public IConstImage, public std::enable_shared_from_this<SDLImageConst>, boost::noncopyable
 {
-	const static int DEFAULT_PALETTE_COLORS = 256;
+	static constexpr int DEFAULT_PALETTE_COLORS = 256;
 	
 	//Surface without empty borders
 	SDL_Surface * surf;
@@ -37,40 +37,78 @@ class SDLImage : public IImage
 	//total size including borders
 	Point fullSize;
 
-	EImageBlitMode blitMode;
+	// Keep the original palette, in order to do color switching operation
+	void savePalette();
 
 public:
 	//Load image from def file
-	SDLImage(CDefFile *data, size_t frame, size_t group=0);
+	SDLImageConst(CDefFile *data, size_t frame, size_t group=0);
 	//Load from bitmap file
-	SDLImage(const ImagePath & filename, EImageBlitMode blitMode);
-
+	SDLImageConst(const ImagePath & filename, EImageBlitMode blitMode);
 	//Create using existing surface, extraRef will increase refcount on SDL_Surface
-	SDLImage(SDL_Surface * from, EImageBlitMode blitMode);
-	~SDLImage();
+	SDLImageConst(SDL_Surface * from, EImageBlitMode blitMode);
+	~SDLImageConst();
 
-	// Keep the original palette, in order to do color switching operation
-	void savePalette();
+	void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, uint8_t alpha, EImageBlitMode mode) const;
 
-	void draw(SDL_Surface * where, int posX=0, int posY=0, const Rect *src=nullptr) const override;
-	void draw(SDL_Surface * where, const Rect * dest, const Rect * src) const override;
-	std::shared_ptr<IImage> scaleFast(const Point & size) const override;
 	void exportBitmap(const boost::filesystem::path & path) const override;
-	void playerColored(PlayerColor player) override;
-	void setFlagColor(PlayerColor player) override;
-	bool isTransparent(const Point & coords) const override;
 	Point dimensions() const override;
+	bool isTransparent(const Point & coords) const override;
+	std::shared_ptr<IImage> createImageReference() override;
+	std::shared_ptr<SDLImageConst> horizontalFlip() const;
+	std::shared_ptr<SDLImageConst> verticalFlip() const;
+	std::shared_ptr<SDLImageConst> scaleFast(const Point & size) const;
+
+	const SDL_Palette * getPalette() const;
 
+	friend class SDLImageLoader;
+};
+
+class SDLImageBase : public IImage, boost::noncopyable
+{
+protected:
+	std::shared_ptr<SDLImageConst> image;
+
+	uint8_t alphaValue;
+	EImageBlitMode blitMode;
+
+public:
+	SDLImageBase(const std::shared_ptr<SDLImageConst> & image);
+
+	void scaleFast(const Point & size) override;
+	void exportBitmap(const boost::filesystem::path & path) const override;
+	bool isTransparent(const Point & coords) const override;
+	Point dimensions() const override;
+	void setAlpha(uint8_t value) override;
+	void setBlitMode(EImageBlitMode mode) override;
 	void horizontalFlip() override;
 	void verticalFlip() override;
+};
+
+class SDLImageIndexed final : public SDLImageBase
+{
+	SDL_Palette * currentPalette = nullptr;
 
+public:
+	SDLImageIndexed(const std::shared_ptr<SDLImageConst> & image);
+	~SDLImageIndexed();
+
+	void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override;
+	void setSpecialPallete(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) override;
+	void playerColored(PlayerColor player) override;
+	void setFlagColor(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 setAlpha(uint8_t value) override;
-	void setBlitMode(EImageBlitMode mode) override;
-
-	void setSpecialPalette(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) override;
+class SDLImageRGB final : public SDLImageBase
+{
+public:
+	using SDLImageBase::SDLImageBase;
 
-	friend class SDLImageLoader;
+	void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override;
+	void playerColored(PlayerColor player) override;
+	void setFlagColor(PlayerColor player) override;
+	void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override;
+	void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override;
 };

+ 3 - 3
client/renderSDL/SDLImageLoader.cpp

@@ -17,7 +17,7 @@
 
 #include <SDL_surface.h>
 
-SDLImageLoader::SDLImageLoader(SDLImage * Img):
+SDLImageLoader::SDLImageLoader(SDLImageConst * Img):
 	image(Img),
 	lineStart(nullptr),
 	position(nullptr)
@@ -32,8 +32,8 @@ void SDLImageLoader::init(Point SpriteSize, Point Margins, Point FullSize, SDL_C
 	image->fullSize = FullSize;
 
 	//Prepare surface
-	SDL_Palette * p = SDL_AllocPalette(SDLImage::DEFAULT_PALETTE_COLORS);
-	SDL_SetPaletteColors(p, pal, 0, SDLImage::DEFAULT_PALETTE_COLORS);
+	SDL_Palette * p = SDL_AllocPalette(SDLImageConst::DEFAULT_PALETTE_COLORS);
+	SDL_SetPaletteColors(p, pal, 0, SDLImageConst::DEFAULT_PALETTE_COLORS);
 	SDL_SetSurfacePalette(image->surf, p);
 	SDL_FreePalette(p);
 

+ 4 - 2
client/renderSDL/SDLImageLoader.h

@@ -11,9 +11,11 @@
 
 #include "../render/IImageLoader.h"
 
+class SDLImageConst;
+
 class SDLImageLoader : public IImageLoader
 {
-	SDLImage * image;
+	SDLImageConst * image;
 	ui8 * lineStart;
 	ui8 * position;
 public:
@@ -25,7 +27,7 @@ public:
 	//init image with these sizes and palette
 	void init(Point SpriteSize, Point Margins, Point FullSize, SDL_Color *pal);
 
-	SDLImageLoader(SDLImage * Img);
+	SDLImageLoader(SDLImageConst * Img);
 	~SDLImageLoader();
 };
 

+ 0 - 17
client/renderSDL/SDL_Extensions.cpp

@@ -524,23 +524,6 @@ void CSDL_Ext::drawBorder( SDL_Surface * sur, const Rect &r, const SDL_Color &co
 	drawBorder(sur, r.x, r.y, r.w, r.h, color, depth);
 }
 
-void CSDL_Ext::setPlayerColor(SDL_Surface * sur, const PlayerColor & player)
-{
-	if(player==PlayerColor::UNFLAGGABLE)
-		return;
-	if(sur->format->BitsPerPixel==8)
-	{
-		ColorRGBA color = (player == PlayerColor::NEUTRAL
-							? graphics->neutralColor
-							: graphics->playerColors[player.getNum()]);
-
-		SDL_Color colorSDL = toSDL(color);
-		CSDL_Ext::setColors(sur, &colorSDL, 5, 1);
-	}
-	else
-		logGlobal->warn("Warning, setPlayerColor called on not 8bpp surface!");
-}
-
 CSDL_Ext::TColorPutter CSDL_Ext::getPutterFor(SDL_Surface * const &dest, int incrementing)
 {
 #define CASE_BPP(BytesPerPixel)							\

+ 0 - 1
client/renderSDL/SDL_Extensions.h

@@ -86,7 +86,6 @@ using TColorPutterAlpha = void (*)(uint8_t *&, const uint8_t &, const uint8_t &,
 
 	void drawBorder(SDL_Surface * sur, int x, int y, int w, int h, const SDL_Color & color, int depth = 1);
 	void drawBorder(SDL_Surface * sur, const Rect & r, const SDL_Color & color, int depth = 1);
-	void setPlayerColor(SDL_Surface * sur, const PlayerColor & player); //sets correct color of flags; -1 for neutral
 
 	SDL_Surface * newSurface(int w, int h, SDL_Surface * mod); //creates new surface, with flags/format same as in surface given
 	SDL_Surface * newSurface(int w, int h); //creates new surface, with flags/format same as in screen surface

+ 4 - 7
client/widgets/Images.cpp

@@ -100,7 +100,7 @@ void CPicture::setAlpha(uint8_t value)
 
 void CPicture::scaleTo(Point size)
 {
-	bg = bg->scaleFast(size);
+	bg->scaleFast(size);
 
 	pos.w = bg->width();
 	pos.h = bg->height();
@@ -262,12 +262,9 @@ void CAnimImage::showAll(Canvas & to)
 		if(auto img = anim->getImage(targetFrame, group))
 		{
 			if(isScaled())
-			{
-				auto scaled = img->scaleFast(scaledSize);
-				to.draw(scaled, pos.topLeft());
-			}
-			else
-				to.draw(img, pos.topLeft());
+				img->scaleFast(scaledSize);
+
+			to.draw(img, pos.topLeft());
 		}
 	}
 }

+ 3 - 3
client/windows/CCastleInterface.cpp

@@ -885,9 +885,9 @@ void CCastleBuildings::enterCastleGate()
 			availableTowns.push_back(t->id.getNum());//add to the list
 			if(settings["general"]["enableUiEnhancements"].Bool())
 			{
-				std::shared_ptr<CAnimation> a = GH.renderHandler().loadAnimation(AnimationPath::builtin("ITPA"));
-				a->preload();
-				images.push_back(a->getImage(t->town->clientInfo.icons[t->hasFort()][false] + 2)->scaleFast(Point(35, 23)));
+				auto image = GH.renderHandler().loadImage(AnimationPath::builtin("ITPA"), t->town->clientInfo.icons[t->hasFort()][false] + 2, 0);
+				image->scaleFast(Point(35, 23));
+				images.push_back(image);
 			}
 		}
 	}

+ 4 - 3
client/windows/CWindowWithArtifacts.cpp

@@ -237,9 +237,10 @@ void CWindowWithArtifacts::setCursorAnimation(const CArtifactInstance & artInst)
 	if(artInst.isScroll() && settings["general"]["enableUiEnhancements"].Bool())
 	{
 		assert(artInst.getScrollSpellID().num >= 0);
-		const auto animation = GH.renderHandler().loadAnimation(AnimationPath::builtin("spellscr"));
-		animation->load(artInst.getScrollSpellID().num);
-		CCS->curh->dragAndDropCursor(animation->getImage(artInst.getScrollSpellID().num)->scaleFast(Point(44, 34)));
+		auto image = GH.renderHandler().loadImage(AnimationPath::builtin("spellscr"), artInst.getScrollSpellID().num, 0);
+		image->scaleFast(Point(44,34));
+
+		CCS->curh->dragAndDropCursor(image);
 	}
 	else
 	{