瀏覽代碼

Remove access to internal surface of Canvas

Ivan Savenko 9 月之前
父節點
當前提交
668bf63fc0

+ 2 - 0
client/CMakeLists.txt

@@ -88,6 +88,7 @@ set(vcmiclientcommon_SRCS
 	render/CBitmapHandler.cpp
 	render/CDefFile.cpp
 	render/Canvas.cpp
+	render/CanvasImage.cpp
 	render/ColorFilter.cpp
 	render/Colors.cpp
 	render/Graphics.cpp
@@ -290,6 +291,7 @@ set(vcmiclientcommon_HEADERS
 	render/CBitmapHandler.h
 	render/CDefFile.h
 	render/Canvas.h
+	render/CanvasImage.h
 	render/ColorFilter.h
 	render/Colors.h
 	render/EFont.h

+ 1 - 1
client/CPlayerInterface.cpp

@@ -1153,7 +1153,7 @@ void CPlayerInterface::showMapObjectSelectDialog(QueryID askID, const Component
 		if(t)
 		{
 			auto image = GH.renderHandler().loadImage(AnimationPath::builtin("ITPA"), t->getTown()->clientInfo.icons[t->hasFort()][false] + 2, 0, EImageBlitMode::OPAQUE);
-			image->scaleTo(Point(35, 23));
+			image->scaleTo(Point(35, 23), EScalingAlgorithm::NEAREST);
 			images.push_back(image);
 		}
 	}

+ 1 - 2
client/adventureMap/CMinimap.cpp

@@ -22,7 +22,6 @@
 #include "../render/Colors.h"
 #include "../render/Canvas.h"
 #include "../render/Graphics.h"
-#include "../renderSDL/SDL_Extensions.h"
 #include "../windows/InfoWindows.h"
 
 #include "../../CCallback.h"
@@ -178,7 +177,7 @@ void CMinimap::mouseDragged(const Point & cursorPosition, const Point & lastUpda
 
 void CMinimap::showAll(Canvas & to)
 {
-	CSDL_Ext::CClipRectGuard guard(to.getInternalSurface(), aiShield->pos);
+	CanvasClipRectGuard guard(to, aiShield->pos);
 	CIntObject::showAll(to);
 
 	if(minimap)

+ 1 - 2
client/battle/BattleFieldController.cpp

@@ -26,7 +26,6 @@
 #include "../render/CAnimation.h"
 #include "../render/Canvas.h"
 #include "../render/IImage.h"
-#include "../renderSDL/SDL_Extensions.h"
 #include "../render/IRenderHandler.h"
 #include "../gui/CGuiHandler.h"
 #include "../gui/CursorHandler.h"
@@ -857,7 +856,7 @@ void BattleFieldController::tick(uint32_t msPassed)
 
 void BattleFieldController::show(Canvas & to)
 {
-	CSDL_Ext::CClipRectGuard guard(to.getInternalSurface(), pos);
+	CanvasClipRectGuard guard(to, pos);
 
 	renderBattlefield(to);
 

+ 2 - 3
client/mapView/MapView.cpp

@@ -24,7 +24,6 @@
 #include "../render/CAnimation.h"
 #include "../render/Canvas.h"
 #include "../render/IImage.h"
-#include "../renderSDL/SDL_Extensions.h"
 #include "../eventsSDL/InputHandler.h"
 
 #include "../../CCallback.h"
@@ -76,7 +75,7 @@ void BasicMapView::tick(uint32_t msPassed)
 
 void BasicMapView::show(Canvas & to)
 {
-	CSDL_Ext::CClipRectGuard guard(to.getInternalSurface(), pos);
+	CanvasClipRectGuard guard(to, pos);
 	render(to, false);
 
 	controller->afterRender();
@@ -84,7 +83,7 @@ void BasicMapView::show(Canvas & to)
 
 void BasicMapView::showAll(Canvas & to)
 {
-	CSDL_Ext::CClipRectGuard guard(to.getInternalSurface(), pos);
+	CanvasClipRectGuard guard(to, pos);
 	render(to, true);
 }
 

+ 2 - 2
client/media/CVideoHandler.cpp

@@ -381,12 +381,12 @@ Point CVideoInstance::size()
 	return dimensions / GH.screenHandler().getScalingFactor();
 }
 
-void CVideoInstance::show(const Point & position, Canvas & canvas)
+void CVideoInstance::show(const Point & position, SDL_Surface * to)
 {
 	if(sws == nullptr)
 		throw std::runtime_error("No video to show!");
 
-	CSDL_Ext::blitSurface(surface, canvas.getInternalSurface(), position * GH.screenHandler().getScalingFactor());
+	CSDL_Ext::blitSurface(surface, to, position * GH.screenHandler().getScalingFactor());
 }
 
 double FFMpegStream::getCurrentFrameEndTime() const

+ 1 - 1
client/media/CVideoHandler.h

@@ -98,7 +98,7 @@ public:
 	bool videoEnded() final;
 	Point size() final;
 
-	void show(const Point & position, Canvas & canvas) final;
+	void show(const Point & position, SDL_Surface * to) final;
 	void tick(uint32_t msPassed) final;
 	void activate() final;
 	void deactivate() final;

+ 2 - 2
client/media/IVideoPlayer.h

@@ -11,7 +11,7 @@
 
 #include "../lib/filesystem/ResourcePath.h"
 
-class Canvas;
+struct SDL_Surface;
 
 VCMI_LIB_NAMESPACE_BEGIN
 class Point;
@@ -30,7 +30,7 @@ public:
 	virtual Point size() = 0;
 
 	/// Displays current frame at specified position
-	virtual void show(const Point & position, Canvas & canvas) = 0;
+	virtual void show(const Point & position, SDL_Surface * to) = 0;
 
 	/// Advances video playback by specified duration
 	virtual void tick(uint32_t msPassed) = 0;

+ 13 - 15
client/render/AssetGenerator.cpp

@@ -14,6 +14,7 @@
 #include "../render/IImage.h"
 #include "../render/IImageLoader.h"
 #include "../render/Canvas.h"
+#include "../render/CanvasImage.h"
 #include "../render/ColorFilter.h"
 #include "../render/IRenderHandler.h"
 #include "../render/CAnimation.h"
@@ -62,7 +63,9 @@ void AssetGenerator::createAdventureOptionsCleanBackground()
 
 	std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator);
 
-	Canvas canvas = Canvas(Point(575, 585), CanvasScalingPolicy::IGNORE);
+	auto image = GH.renderHandler().createImage(Point(575, 585), CanvasScalingPolicy::IGNORE);
+	Canvas canvas = image->getCanvas();
+
 	canvas.draw(img, Point(0, 0), Rect(0, 0, 575, 585));
 	canvas.draw(img, Point(54, 121), Rect(54, 123, 335, 1));
 	canvas.draw(img, Point(158, 84), Rect(156, 84, 2, 37));
@@ -71,8 +74,6 @@ void AssetGenerator::createAdventureOptionsCleanBackground()
 	canvas.draw(img, Point(53, 567), Rect(53, 520, 339, 3));
 	canvas.draw(img, Point(53, 520), Rect(53, 264, 339, 47));
 
-	std::shared_ptr<IImage> image = GH.renderHandler().createImage(canvas.getInternalSurface());
-
 	image->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePath));
 }
 
@@ -90,7 +91,8 @@ void AssetGenerator::createBigSpellBook()
 	auto locator = ImageLocator(ImagePath::builtin("SpelBack"), EImageBlitMode::OPAQUE);
 
 	std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator);
-	Canvas canvas = Canvas(Point(800, 600), CanvasScalingPolicy::IGNORE);
+	auto image = GH.renderHandler().createImage(Point(800, 600), CanvasScalingPolicy::IGNORE);
+	Canvas canvas = image->getCanvas();
 	// edges
 	canvas.draw(img, Point(0, 0), Rect(15, 38, 90, 45));
 	canvas.draw(img, Point(0, 460), Rect(15, 400, 90, 141));
@@ -133,8 +135,6 @@ void AssetGenerator::createBigSpellBook()
 	canvas.draw(img, Point(575, 465), Rect(417, 406, 37, 45));
 	canvas.draw(img, Point(667, 465), Rect(478, 406, 37, 47));
 
-	std::shared_ptr<IImage> image = GH.renderHandler().createImage(canvas.getInternalSurface());
-
 	image->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePath));
 }
 
@@ -233,8 +233,9 @@ void AssetGenerator::createCampaignBackground()
 	auto locator = ImageLocator(ImagePath::builtin("CAMPBACK"), EImageBlitMode::OPAQUE);
 
 	std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator);
-	Canvas canvas = Canvas(Point(800, 600), CanvasScalingPolicy::IGNORE);
-	
+	auto image = GH.renderHandler().createImage(Point(800, 600), CanvasScalingPolicy::IGNORE);
+	Canvas canvas = image->getCanvas();
+
 	canvas.draw(img, Point(0, 0), Rect(0, 0, 800, 600));
 
 	// left image
@@ -263,8 +264,6 @@ void AssetGenerator::createCampaignBackground()
 	std::shared_ptr<IImage> imgSkull = GH.renderHandler().loadImage(locatorSkull);
 	canvas.draw(imgSkull, Point(562, 509), Rect(178, 108, 43, 19));
 
-	std::shared_ptr<IImage> image = GH.renderHandler().createImage(canvas.getInternalSurface());
-
 	image->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePath));
 }
 
@@ -288,7 +287,8 @@ void AssetGenerator::createChroniclesCampaignImages()
 		auto locator = ImageLocator(imgPathBg, EImageBlitMode::OPAQUE);
 
 		std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator);
-		Canvas canvas = Canvas(Point(200, 116), CanvasScalingPolicy::IGNORE);
+		auto image = GH.renderHandler().createImage(Point(800, 600), CanvasScalingPolicy::IGNORE);
+		Canvas canvas = image->getCanvas();
 		
 		switch (i)
 		{
@@ -327,8 +327,6 @@ void AssetGenerator::createChroniclesCampaignImages()
 			break;
 		}
 
-		std::shared_ptr<IImage> image = GH.renderHandler().createImage(canvas.getInternalSurface());
-
 		image->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePath));
 	}
 }
@@ -412,9 +410,9 @@ void AssetGenerator::createPaletteShiftedSprites()
 					}
 				}
 				
-				Canvas canvas = Canvas(Point(32, 32), CanvasScalingPolicy::IGNORE);
+				auto image = GH.renderHandler().createImage(Point(32, 32), CanvasScalingPolicy::IGNORE);
+				Canvas canvas = image->getCanvas();
 				canvas.draw(img, Point((32 - img->dimensions().x) / 2, (32 - img->dimensions().y) / 2));
-				std::shared_ptr<IImage> image = GH.renderHandler().createImage(canvas.getInternalSurface());
 				image->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePath));
 
 				JsonNode node(JsonMap{

+ 21 - 4
client/render/Canvas.cpp

@@ -11,6 +11,7 @@
 #include "Canvas.h"
 
 #include "../gui/CGuiHandler.h"
+#include "../media/IVideoPlayer.h"
 #include "../render/IRenderHandler.h"
 #include "../render/IScreenHandler.h"
 #include "../renderSDL/SDL_Extensions.h"
@@ -102,6 +103,16 @@ Canvas::~Canvas()
 	SDL_FreeSurface(surface);
 }
 
+void Canvas::draw(IVideoInstance & video, const Point & pos)
+{
+	video.show(pos, surface);
+}
+
+void Canvas::draw(const IImage& image, const Point & pos)
+{
+	image.draw(surface, transformPos(pos), nullptr, getScalingFactor());
+}
+
 void Canvas::draw(const std::shared_ptr<IImage>& image, const Point & pos)
 {
 	assert(image);
@@ -222,12 +233,18 @@ void Canvas::fillTexture(const std::shared_ptr<IImage>& image)
 	}
 }
 
-SDL_Surface * Canvas::getInternalSurface() const
+Rect Canvas::getRenderArea() const
 {
-	return surface;
+	return renderArea;
 }
 
-Rect Canvas::getRenderArea() const
+CanvasClipRectGuard::CanvasClipRectGuard(Canvas & canvas, const Rect & rect): surf(canvas.surface)
 {
-	return renderArea;
+	CSDL_Ext::getClipRect(surf, oldRect);
+	CSDL_Ext::setClipRect(surf, rect * GH.screenHandler().getScalingFactor());
+}
+
+CanvasClipRectGuard::~CanvasClipRectGuard()
+{
+	CSDL_Ext::setClipRect(surf, oldRect);
 }

+ 16 - 3
client/render/Canvas.h

@@ -15,6 +15,7 @@
 
 struct SDL_Surface;
 class IImage;
+class IVideoInstance;
 enum EFonts : int8_t;
 
 enum class CanvasScalingPolicy
@@ -27,6 +28,8 @@ enum class CanvasScalingPolicy
 /// Class that represents surface for drawing on
 class Canvas
 {
+	friend class CanvasClipRectGuard;
+
 	/// Upscaler awareness. Must be first member for initialization
 	CanvasScalingPolicy scalingPolicy;
 
@@ -72,6 +75,9 @@ public:
 
 	/// renders image onto this canvas at specified position
 	void draw(const std::shared_ptr<IImage>& image, const Point & pos);
+	void draw(const IImage& image, const Point & pos);
+
+	void draw(IVideoInstance & video, const Point & pos);
 
 	/// renders section of image bounded by sourceRect at specified position
 	void draw(const std::shared_ptr<IImage>& image, const Point & pos, const Rect & sourceRect);
@@ -114,9 +120,16 @@ public:
 
 	int getScalingFactor() const;
 
-	/// Compatibility method. AVOID USAGE. To be removed once SDL abstraction layer is finished.
-	SDL_Surface * getInternalSurface() const;
-
 	/// get the render area
 	Rect getRenderArea() const;
 };
+
+class CanvasClipRectGuard : boost::noncopyable
+{
+	SDL_Surface * surf;
+	Rect oldRect;
+
+public:
+	CanvasClipRectGuard(Canvas & canvas, const Rect & rect);
+	~CanvasClipRectGuard();
+};

+ 61 - 0
client/render/CanvasImage.cpp

@@ -0,0 +1,61 @@
+/*
+ * CanvasImage.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+#include "CanvasImage.h"
+
+#include "../gui/CGuiHandler.h"
+#include "../render/IScreenHandler.h"
+#include "../renderSDL/SDL_Extensions.h"
+
+#include <SDL_image.h>
+#include <SDL_surface.h>
+
+CanvasImage::CanvasImage(const Point & size, CanvasScalingPolicy scalingPolicy)
+	: surface(CSDL_Ext::newSurface(scalingPolicy == CanvasScalingPolicy::IGNORE ? size : (size * GH.screenHandler().getScalingFactor())))
+	, scalingPolicy(scalingPolicy)
+{
+}
+
+void CanvasImage::draw(SDL_Surface * where, const Point & pos, const Rect * src, int scalingFactor) const
+{
+	if(src)
+		CSDL_Ext::blitSurface(surface, *src, where, pos);
+	else
+		CSDL_Ext::blitSurface(surface, where, pos);
+}
+
+void CanvasImage::scaleTo(const Point & size, EScalingAlgorithm algorithm)
+{
+	Point scaledSize = scalingPolicy == CanvasScalingPolicy::IGNORE ? size : (size * GH.screenHandler().getScalingFactor());
+
+	auto newSurface = CSDL_Ext::scaleSurface(surface, scaledSize.x, scaledSize.y, algorithm);
+	SDL_FreeSurface(surface);
+	surface = newSurface;
+}
+
+void CanvasImage::exportBitmap(const boost::filesystem::path & path) const
+{
+	IMG_SavePNG(surface, path.string().c_str());
+}
+
+Canvas CanvasImage::getCanvas()
+{
+	return Canvas::createFromSurface(surface, scalingPolicy);
+}
+
+Rect CanvasImage::contentRect() const
+{
+	return Rect(Point(0, 0), dimensions());
+}
+
+Point CanvasImage::dimensions() const
+{
+	return {surface->w, surface->h};
+}

+ 41 - 0
client/render/CanvasImage.h

@@ -0,0 +1,41 @@
+/*
+ * CanvasImage.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "IImage.h"
+#include "Canvas.h"
+
+class CanvasImage : public IImage
+{
+public:
+	CanvasImage(const Point & size, CanvasScalingPolicy scalingPolicy);
+
+	Canvas getCanvas();
+
+	void draw(SDL_Surface * where, const Point & pos, const Rect * src, int scalingFactor) const override;
+	void scaleTo(const Point & size, EScalingAlgorithm algorithm) override;
+	void exportBitmap(const boost::filesystem::path & path) const override;
+	Rect contentRect() const override;
+	Point dimensions() const override;
+
+	//no-op methods
+
+	bool isTransparent(const Point & coords) const override{ return false;};
+	void setAlpha(uint8_t value) override{};
+	void playerColored(const PlayerColor & player) override{};
+	void setOverlayColor(const ColorRGBA & color) override{};
+	void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override{};
+	void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override{};
+
+private:
+	SDL_Surface * surface;
+	CanvasScalingPolicy scalingPolicy;
+};
+

+ 10 - 2
client/render/IImage.h

@@ -62,6 +62,14 @@ enum class EImageBlitMode : uint8_t
 	ONLY_OVERLAY,
 };
 
+enum class EScalingAlgorithm : int8_t
+{
+	NEAREST,
+	BILINEAR,
+	XBRZ_OPAQUE, // xbrz, image edges are considered to have same color as pixel inside image. Only for integer scaling
+	XBRZ_ALPHA // xbrz, image edges are considered to be transparent. Only for integer scaling
+};
+
 /// Base class for images for use in client code.
 /// This class represents current state of image, with potential transformations applied, such as player coloring
 class IImage
@@ -70,12 +78,12 @@ public:
 	//draws image on surface "where" at position
 	virtual void draw(SDL_Surface * where, const Point & pos, const Rect * src, int scalingFactor) const = 0;
 
-	virtual void scaleTo(const Point & size) = 0;
+	virtual void scaleTo(const Point & size, EScalingAlgorithm algorithm) = 0;
 
 	virtual void exportBitmap(const boost::filesystem::path & path) const = 0;
 
 	//Change palette to specific player
-	virtual void playerColored(PlayerColor player) = 0;
+	virtual void playerColored(const PlayerColor & player) = 0;
 
 	//test transparency of specific pixel
 	virtual bool isTransparent(const Point & coords) const = 0;

+ 4 - 3
client/render/IRenderHandler.h

@@ -20,8 +20,10 @@ struct SDL_Surface;
 class IFont;
 class IImage;
 class CAnimation;
+class CanvasImage;
 class SDLImageShared;
 enum class EImageBlitMode : uint8_t;
+enum class CanvasScalingPolicy;
 enum EFonts : int8_t;
 
 class IRenderHandler : public boost::noncopyable
@@ -40,9 +42,8 @@ public:
 	/// Loads single image without scaling support
 	virtual std::shared_ptr<SDLImageShared> loadSingleImage(const ImageLocator & locator) = 0;
 
-	/// temporary compatibility method. Creates IImage from existing SDL_Surface
-	/// Surface will be shared, caller must still free it with SDL_FreeSurface
-	virtual std::shared_ptr<IImage> createImage(SDL_Surface * source) = 0;
+	/// Creates image which can be used as target for drawing on
+	virtual std::shared_ptr<CanvasImage> createImage(const Point & size, CanvasScalingPolicy scalingPolicy) = 0;
 
 	/// Loads animation using given path
 	virtual std::shared_ptr<CAnimation> loadAnimation(const AnimationPath & path, EImageBlitMode mode) = 0;

+ 1 - 0
client/renderSDL/CBitmapFont.cpp

@@ -14,6 +14,7 @@
 #include "../CGameInfo.h"
 #include "../gui/CGuiHandler.h"
 #include "../render/Colors.h"
+#include "../render/IImage.h"
 #include "../render/IScreenHandler.h"
 
 #include "../../lib/CConfigHandler.h"

+ 1 - 1
client/renderSDL/CursorHardware.cpp

@@ -60,7 +60,7 @@ void CursorHardware::setImage(std::shared_ptr<IImage> image, const Point & pivot
 	CSDL_Ext::fillSurface(cursorSurface, CSDL_Ext::toSDL(Colors::TRANSPARENCY));
 
 	image->draw(cursorSurface, Point(0,0), nullptr, GH.screenHandler().getScalingFactor());
-	auto cursorSurfaceScaled = CSDL_Ext::scaleSurface(cursorSurface, cursorDimensionsScaled.x, cursorDimensionsScaled.y );
+	auto cursorSurfaceScaled = CSDL_Ext::scaleSurface(cursorSurface, cursorDimensionsScaled.x, cursorDimensionsScaled.y, EScalingAlgorithm::BILINEAR );
 
 	auto oldCursor = cursor;
 	cursor = SDL_CreateColorCursor(cursorSurfaceScaled, pivotOffsetScaled.x, pivotOffsetScaled.y);

+ 3 - 7
client/renderSDL/RenderHandler.cpp

@@ -17,6 +17,7 @@
 #include "../gui/CGuiHandler.h"
 
 #include "../render/CAnimation.h"
+#include "../render/CanvasImage.h"
 #include "../render/CDefFile.h"
 #include "../render/Colors.h"
 #include "../render/ColorFilter.h"
@@ -308,14 +309,9 @@ std::shared_ptr<IImage> RenderHandler::loadImage(const ImagePath & path, EImageB
 	return loadImage(locator);
 }
 
-std::shared_ptr<IImage> RenderHandler::createImage(SDL_Surface * source)
+std::shared_ptr<CanvasImage> RenderHandler::createImage(const Point & size, CanvasScalingPolicy scalingPolicy)
 {
-	auto baseImage = std::make_shared<SDLImageShared>(source);
-	SharedImageLocator locator;
-	locator.layer = EImageBlitMode::SIMPLE;
-	auto scalableImage = std::make_shared<ScalableImageShared>(locator, baseImage);
-
-	return scalableImage->createImageReference();
+	return std::make_shared<CanvasImage>(size, scalingPolicy);
 }
 
 std::shared_ptr<CAnimation> RenderHandler::loadAnimation(const AnimationPath & path, EImageBlitMode mode)

+ 1 - 1
client/renderSDL/RenderHandler.h

@@ -57,7 +57,7 @@ public:
 
 	std::shared_ptr<CAnimation> loadAnimation(const AnimationPath & path, EImageBlitMode mode) override;
 
-	std::shared_ptr<IImage> createImage(SDL_Surface * source) override;
+	std::shared_ptr<CanvasImage> createImage(const Point & size, CanvasScalingPolicy scalingPolicy) override;
 
 	/// Returns font with specified identifer
 	std::shared_ptr<const IFont> loadFont(EFonts font) override;

+ 1 - 1
client/renderSDL/SDLImage.cpp

@@ -268,7 +268,7 @@ std::shared_ptr<const ISharedImage> SDLImageShared::scaleTo(const Point & size,
 	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), EScalingAlgorithm::BILINEAR);
 
 	if (scaled->format && scaled->format->palette) // fix color keying, because SDL loses it at this point
 		CSDL_Ext::setColorKey(scaled, scaled->format->palette->colors[0]);

+ 10 - 11
client/renderSDL/SDL_Extensions.cpp

@@ -14,6 +14,7 @@
 
 #include "../gui/CGuiHandler.h"
 #include "../render/Graphics.h"
+#include "../render/IImage.h"
 #include "../render/IScreenHandler.h"
 #include "../render/Colors.h"
 #include "../CMT.h"
@@ -633,7 +634,7 @@ void CSDL_Ext::convertToGrayscale( SDL_Surface * surf, const Rect & rect )
 // scaling via bilinear interpolation algorithm.
 // NOTE: best results are for scaling in range 50%...200%.
 // And upscaling looks awful right now - should be fixed somehow
-SDL_Surface * CSDL_Ext::scaleSurface(SDL_Surface * surf, int width, int height)
+SDL_Surface * CSDL_Ext::scaleSurface(SDL_Surface * surf, int width, int height, EScalingAlgorithm algorithm)
 {
 	if(!surf || !width || !height)
 		return nullptr;
@@ -644,11 +645,14 @@ SDL_Surface * CSDL_Ext::scaleSurface(SDL_Surface * surf, int width, int height)
 	SDL_Surface * intermediate = SDL_ConvertSurface(surf, screen->format, 0);
 	SDL_Surface * ret = newSurface(Point(width, height), intermediate);
 
-#if SDL_VERSION_ATLEAST(2,0,16)
-	SDL_SoftStretchLinear(intermediate, nullptr, ret, nullptr);
-#else
-	SDL_SoftStretch(intermediate, nullptr, ret, nullptr);
-#endif
+	const uint32_t * srcPixels = static_cast<const uint32_t*>(intermediate->pixels);
+	uint32_t * dstPixels = static_cast<uint32_t*>(ret->pixels);
+
+	if (algorithm == EScalingAlgorithm::NEAREST)
+		xbrz::nearestNeighborScale(srcPixels, intermediate->w, intermediate->h, dstPixels, ret->w, ret->h);
+	else
+		xbrz::bilinearScale(srcPixels, intermediate->w, intermediate->h, dstPixels, ret->w, ret->h);
+
 	SDL_FreeSurface(intermediate);
 
 	return ret;
@@ -799,10 +803,5 @@ void CSDL_Ext::getClipRect(SDL_Surface * src, Rect & other)
 	other = CSDL_Ext::fromSDL(rect);
 }
 
-int CSDL_Ext::CClipRectGuard::getScalingFactor() const
-{
-	return GH.screenHandler().getScalingFactor();
-}
-
 template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<3>(int, int);
 template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<4>(int, int);

+ 2 - 9
client/renderSDL/SDL_Extensions.h

@@ -18,6 +18,7 @@ struct SDL_Renderer;
 struct SDL_Texture;
 struct SDL_Surface;
 struct SDL_Color;
+enum class EScalingAlgorithm : int8_t;
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -27,14 +28,6 @@ class Point;
 
 VCMI_LIB_NAMESPACE_END
 
-enum class EScalingAlgorithm : int8_t
-{
-	NEAREST,
-	BILINEAR,
-	XBRZ_OPAQUE, // xbrz, image edges are considered to have same color as pixel inside image
-	XBRZ_ALPHA // xbrz, image edges are considered to be transparent
-};
-
 namespace CSDL_Ext
 {
 
@@ -99,7 +92,7 @@ using TColorPutterAlpha = void (*)(uint8_t *&, const uint8_t &, const uint8_t &,
 	SDL_Surface * createSurfaceWithBpp(int width, int height); //create surface with give bits per pixels value
 
 	// bilinear filtering. Always returns rgba surface
-	SDL_Surface * scaleSurface(SDL_Surface * surf, int width, int height);
+	SDL_Surface * scaleSurface(SDL_Surface * surf, int width, int height, EScalingAlgorithm scaler);
 	SDL_Surface * scaleSurfaceIntegerFactor(SDL_Surface * surf, int factor, EScalingAlgorithm scaler);
 
 	template<int bpp>

+ 16 - 4
client/renderSDL/ScalableImage.cpp

@@ -20,6 +20,7 @@
 #include "../render/Graphics.h"
 #include "../render/IRenderHandler.h"
 #include "../render/IScreenHandler.h"
+#include "../render/CanvasImage.h"
 
 #include "../../lib/constants/EntityIdentifiers.h"
 
@@ -292,9 +293,15 @@ ScalableImageInstance::ScalableImageInstance(const std::shared_ptr<ScalableImage
 	assert(image);
 }
 
-void ScalableImageInstance::scaleTo(const Point & size)
+void ScalableImageInstance::scaleTo(const Point & size, EScalingAlgorithm algorithm)
 {
-	assert(0);
+	scaledImage = nullptr;
+
+	auto newScaledImage = GH.renderHandler().createImage(dimensions(), CanvasScalingPolicy::AUTO);
+
+	newScaledImage->getCanvas().draw(*this, Point(0, 0));
+	newScaledImage->scaleTo(size, algorithm);
+	scaledImage = newScaledImage;
 }
 
 void ScalableImageInstance::exportBitmap(const boost::filesystem::path & path) const
@@ -314,6 +321,8 @@ Rect ScalableImageInstance::contentRect() const
 
 Point ScalableImageInstance::dimensions() const
 {
+	if (scaledImage)
+		return scaledImage->dimensions() / GH.screenHandler().getScalingFactor();
 	return image->dimensions();
 }
 
@@ -324,7 +333,10 @@ void ScalableImageInstance::setAlpha(uint8_t value)
 
 void ScalableImageInstance::draw(SDL_Surface * where, const Point & pos, const Rect * src, int scalingFactor) const
 {
-	image->draw(where, pos, src, parameters, scalingFactor);
+	if (scaledImage)
+		scaledImage->draw(where, pos, src, scalingFactor);
+	else
+		image->draw(where, pos, src, parameters, scalingFactor);
 }
 
 void ScalableImageInstance::setOverlayColor(const ColorRGBA & color)
@@ -335,7 +347,7 @@ void ScalableImageInstance::setOverlayColor(const ColorRGBA & color)
 		parameters.setOverlayColor(image->getPalette(), color);
 }
 
-void ScalableImageInstance::playerColored(PlayerColor player)
+void ScalableImageInstance::playerColored(const PlayerColor & player)
 {
 	parameters.player = player;
 

+ 4 - 2
client/renderSDL/ScalableImage.h

@@ -18,6 +18,7 @@
 struct SDL_Palette;
 
 class ScalableImageInstance;
+class CanvasImage;
 
 struct ScalableImageParameters : boost::noncopyable
 {
@@ -99,6 +100,7 @@ class ScalableImageInstance final : public IImage
 	friend class ScalableImageShared;
 
 	std::shared_ptr<ScalableImageShared> image;
+	std::shared_ptr<CanvasImage> scaledImage;
 
 	ScalableImageParameters parameters;
 	EImageBlitMode blitMode;
@@ -106,7 +108,7 @@ class ScalableImageInstance final : public IImage
 public:
 	ScalableImageInstance(const std::shared_ptr<ScalableImageShared> & image, EImageBlitMode blitMode);
 
-	void scaleTo(const Point & size) override;
+	void scaleTo(const Point & size, EScalingAlgorithm algorithm) override;
 	void exportBitmap(const boost::filesystem::path & path) const override;
 	bool isTransparent(const Point & coords) const override;
 	Rect contentRect() const override;
@@ -114,7 +116,7 @@ public:
 	void setAlpha(uint8_t value) override;
 	void draw(SDL_Surface * where, const Point & pos, const Rect * src, int scalingFactor) const override;
 	void setOverlayColor(const ColorRGBA & color) override;
-	void playerColored(PlayerColor player) override;
+	void playerColored(const PlayerColor & player) override;
 	void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override;
 	void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override;
 

+ 5 - 4
client/renderSDL/ScreenHandler.cpp

@@ -11,13 +11,14 @@
 #include "StdInc.h"
 #include "ScreenHandler.h"
 
-#include "../../lib/CConfigHandler.h"
-#include "../../lib/constants/StringConstants.h"
-#include "../gui/CGuiHandler.h"
 #include "../eventsSDL/NotificationHandler.h"
+#include "../gui/CGuiHandler.h"
 #include "../gui/WindowHandler.h"
+#include "../renderSDL/SDL_Extensions.h"
 #include "CMT.h"
-#include "SDL_Extensions.h"
+
+#include "../../lib/CConfigHandler.h"
+#include "../../lib/constants/StringConstants.h"
 
 #ifdef VCMI_ANDROID
 #include "../lib/CAndroidVMHelper.h"

+ 3 - 4
client/widgets/Images.cpp

@@ -13,7 +13,6 @@
 #include "MiscWidgets.h"
 
 #include "../gui/CGuiHandler.h"
-#include "../renderSDL/SDL_Extensions.h"
 #include "../render/AssetGenerator.h"
 #include "../render/IImage.h"
 #include "../render/IRenderHandler.h"
@@ -122,7 +121,7 @@ void CPicture::setAlpha(uint8_t value)
 
 void CPicture::scaleTo(Point size)
 {
-	bg->scaleTo(size);
+	bg->scaleTo(size, EScalingAlgorithm::BILINEAR);
 
 	pos.w = bg->width();
 	pos.h = bg->height();
@@ -164,7 +163,7 @@ CFilledTexture::CFilledTexture(const ImagePath & imageName, Rect position, Rect
 
 void CFilledTexture::showAll(Canvas & to)
 {
-	CSDL_Ext::CClipRectGuard guard(to.getInternalSurface(), pos);
+	CanvasClipRectGuard guard(to, pos);
 
 	for (int y=pos.top(); y < pos.bottom(); y+= imageArea.h)
 	{
@@ -270,7 +269,7 @@ void CAnimImage::showAll(Canvas & to)
 		if(auto img = anim->getImage(targetFrame, group))
 		{
 			if(isScaled())
-				img->scaleTo(scaledSize);
+				img->scaleTo(scaledSize, EScalingAlgorithm::BILINEAR);
 
 			to.draw(img, pos.topLeft());
 		}

+ 1 - 2
client/widgets/TextControls.cpp

@@ -18,7 +18,6 @@
 #include "../windows/CMessage.h"
 #include "../windows/InfoWindows.h"
 #include "../adventureMap/CInGameConsole.h"
-#include "../renderSDL/SDL_Extensions.h"
 #include "../render/Canvas.h"
 #include "../render/Graphics.h"
 #include "../render/IFont.h"
@@ -298,7 +297,7 @@ void CMultiLineLabel::showAll(Canvas & to)
 	Point lineStart = getTextLocation().topLeft() - visibleSize + Point(0, beginLine * fontPtr->getLineHeight());
 	Point lineSize = Point(getTextLocation().w, fontPtr->getLineHeight());
 
-	CSDL_Ext::CClipRectGuard guard(to.getInternalSurface(), getTextLocation()); // to properly trim text that is too big to fit
+	CanvasClipRectGuard guard(to, getTextLocation()); // to properly trim text that is too big to fit
 
 	for(int i = beginLine; i < std::min(totalLines, endLine); i++)
 	{

+ 2 - 2
client/widgets/VideoWidget.cpp

@@ -86,7 +86,7 @@ void VideoWidgetBase::playVideo(const VideoPath & fileToPlay)
 void VideoWidgetBase::show(Canvas & to)
 {
 	if(videoInstance)
-		videoInstance->show(pos.topLeft(), to);
+		to.draw(*videoInstance, pos.topLeft());
 	if(subTitle)
 		subTitle->showAll(to);
 }
@@ -162,7 +162,7 @@ void VideoWidgetBase::deactivate()
 void VideoWidgetBase::showAll(Canvas & to)
 {
 	if(videoInstance)
-		videoInstance->show(pos.topLeft(), to);
+		to.draw(*videoInstance, pos.topLeft());
 	if(subTitle)
 		subTitle->showAll(to);
 }

+ 1 - 1
client/windows/CCastleInterface.cpp

@@ -973,7 +973,7 @@ void CCastleBuildings::enterCastleGate(BuildingID building)
 			if(settings["general"]["enableUiEnhancements"].Bool())
 			{
 				auto image = GH.renderHandler().loadImage(AnimationPath::builtin("ITPA"), t->getTown()->clientInfo.icons[t->hasFort()][false] + 2, 0, EImageBlitMode::OPAQUE);
-				image->scaleTo(Point(35, 23));
+				image->scaleTo(Point(35, 23), EScalingAlgorithm::NEAREST);
 				images.push_back(image);
 			}
 		}

+ 14 - 14
client/windows/CMapOverview.cpp

@@ -20,7 +20,7 @@
 #include "../widgets/TextControls.h"
 #include "../windows/GUIClasses.h"
 #include "../windows/InfoWindows.h"
-#include "../render/Canvas.h"
+#include "../render/CanvasImage.h"
 #include "../render/IImage.h"
 #include "../render/IRenderHandler.h"
 #include "../render/Graphics.h"
@@ -58,9 +58,10 @@ CMapOverview::CMapOverview(const std::string & mapName, const std::string & file
 	fitToScreen(10);
 }
 
-Canvas CMapOverviewWidget::createMinimapForLayer(std::unique_ptr<CMap> & map, int layer) const
+std::shared_ptr<CanvasImage> CMapOverviewWidget::createMinimapForLayer(std::unique_ptr<CMap> & map, int layer) const
 {
-	Canvas canvas = Canvas(Point(map->width, map->height), CanvasScalingPolicy::IGNORE);
+	auto canvasImage = GH.renderHandler().createImage(Point(map->width, map->height), CanvasScalingPolicy::IGNORE);
+	auto canvas = canvasImage->getCanvas();
 
 	for (int y = 0; y < map->height; ++y)
 		for (int x = 0; x < map->width; ++x)
@@ -91,12 +92,12 @@ Canvas CMapOverviewWidget::createMinimapForLayer(std::unique_ptr<CMap> & map, in
 			canvas.drawPoint(Point(x, y), color);
 		}
 	
-	return canvas;
+	return canvasImage;
 }
 
-std::vector<Canvas> CMapOverviewWidget::createMinimaps(ResourcePath resource) const
+std::vector<std::shared_ptr<CanvasImage>> CMapOverviewWidget::createMinimaps(ResourcePath resource) const
 {
-	auto ret = std::vector<Canvas>();
+	std::vector<std::shared_ptr<CanvasImage>> ret;
 
 	CMapService mapService;
 	std::unique_ptr<CMap> map;
@@ -113,9 +114,9 @@ std::vector<Canvas> CMapOverviewWidget::createMinimaps(ResourcePath resource) co
 	return createMinimaps(map);
 }
 
-std::vector<Canvas> CMapOverviewWidget::createMinimaps(std::unique_ptr<CMap> & map) const
+std::vector<std::shared_ptr<CanvasImage>> CMapOverviewWidget::createMinimaps(std::unique_ptr<CMap> & map) const
 {
-	auto ret = std::vector<Canvas>();
+	std::vector<std::shared_ptr<CanvasImage>> ret;
 
 	for(int i = 0; i < (map->twoLevel ? 2 : 1); i++)
 		ret.push_back(createMinimapForLayer(map, i));
@@ -133,16 +134,15 @@ std::shared_ptr<CPicture> CMapOverviewWidget::buildDrawMinimap(const JsonNode &
 	if(id >= minimaps.size())
 		return nullptr;
 
-	Rect minimapRect = minimaps[id].getRenderArea();
-	double maxSideLengthSrc = std::max(minimapRect.w, minimapRect.h);
+	Point minimapRect = minimaps[id]->dimensions();
+	double maxSideLengthSrc = std::max(minimapRect.x, minimapRect.y);
 	double maxSideLengthDst = std::max(rect.w, rect.h);
 	double resize = maxSideLengthSrc / maxSideLengthDst;
-	Point newMinimapSize = Point(minimapRect.w / resize, minimapRect.h / resize);
+	Point newMinimapSize = Point(minimapRect.x / resize, minimapRect.y / resize);
 
-	std::shared_ptr<IImage> img = GH.renderHandler().createImage(minimaps[id].getInternalSurface());
-	img->scaleTo(newMinimapSize);
+	minimaps[id]->scaleTo(newMinimapSize, EScalingAlgorithm::NEAREST); // for sharp-looking minimap
 
-	return std::make_shared<CPicture>(img, Point(rect.x, rect.y));
+	return std::make_shared<CPicture>(minimaps[id], Point(rect.x, rect.y));
 }
 
 CMapOverviewWidget::CMapOverviewWidget(CMapOverview& parent):

+ 5 - 5
client/windows/CMapOverview.h

@@ -22,7 +22,7 @@ class CPicture;
 class CFilledTexture;
 class CTextBox;
 class IImage;
-class Canvas;
+class CanvasImage;
 class TransparentFilledRectangle;
 enum class ESelectionScreen : ui8;
 
@@ -33,11 +33,11 @@ class CMapOverviewWidget : public InterfaceObjectConfigurable
 	CMapOverview& p;
 
 	bool drawPlayerElements;
-	std::vector<Canvas> minimaps;
+	std::vector<std::shared_ptr<CanvasImage>> minimaps;
 
-	Canvas createMinimapForLayer(std::unique_ptr<CMap> & map, int layer) const;
-	std::vector<Canvas> createMinimaps(ResourcePath resource) const;
-	std::vector<Canvas> createMinimaps(std::unique_ptr<CMap> & map) const;
+	std::shared_ptr<CanvasImage> createMinimapForLayer(std::unique_ptr<CMap> & map, int layer) const;
+	std::vector<std::shared_ptr<CanvasImage>> createMinimaps(ResourcePath resource) const;
+	std::vector<std::shared_ptr<CanvasImage>> createMinimaps(std::unique_ptr<CMap> & map) const;
 
 	std::shared_ptr<CPicture> buildDrawMinimap(const JsonNode & config) const;
 public:

+ 1 - 2
client/windows/CQuestLog.cpp

@@ -21,7 +21,6 @@
 #include "../adventureMap/AdventureMapInterface.h"
 #include "../adventureMap/CMinimap.h"
 #include "../render/Canvas.h"
-#include "../renderSDL/SDL_Extensions.h"
 
 #include "../../CCallback.h"
 #include "../../lib/CArtHandler.h"
@@ -61,7 +60,7 @@ void CQuestIcon::clickPressed(const Point & cursorPosition)
 
 void CQuestIcon::showAll(Canvas & to)
 {
-	CSDL_Ext::CClipRectGuard guard(to.getInternalSurface(), parent->pos);
+	CanvasClipRectGuard guard(to, parent->pos);
 	CAnimImage::showAll(to);
 }
 

+ 22 - 60
client/windows/CWindowObject.cpp

@@ -23,6 +23,7 @@
 #include "../render/IScreenHandler.h"
 #include "../render/IRenderHandler.h"
 #include "../render/Canvas.h"
+#include "../render/CanvasImage.h"
 
 #include "../CGameInfo.h"
 #include "../CPlayerInterface.h"
@@ -128,55 +129,6 @@ void CWindowObject::setShadow(bool on)
 
 	if(on)
 	{
-
-		//helper to set last row
-		auto blitAlphaRow = [](SDL_Surface *surf, size_t row)
-		{
-			uint8_t * ptr = (uint8_t*)surf->pixels + surf->pitch * (row);
-
-			for (size_t i=0; i< surf->w; i++)
-			{
-				Channels::px<4>::a.set(ptr, 128);
-				ptr+=4;
-			}
-		};
-
-		// helper to set last column
-		auto blitAlphaCol = [](SDL_Surface *surf, size_t col)
-		{
-			uint8_t * ptr = (uint8_t*)surf->pixels + 4 * (col);
-
-			for (size_t i=0; i< surf->h; i++)
-			{
-				Channels::px<4>::a.set(ptr, 128);
-				ptr+= surf->pitch;
-			}
-		};
-
-		static SDL_Surface * shadowCornerTempl = nullptr;
-		static SDL_Surface * shadowBottomTempl = nullptr;
-		static SDL_Surface * shadowRightTempl = nullptr;
-
-		//one-time initialization
-		if(!shadowCornerTempl)
-		{
-			//create "template" surfaces
-			shadowCornerTempl = CSDL_Ext::createSurfaceWithBpp<4>(size, size);
-			shadowBottomTempl = CSDL_Ext::createSurfaceWithBpp<4>(1, size);
-			shadowRightTempl  = CSDL_Ext::createSurfaceWithBpp<4>(size, 1);
-
-			//fill with shadow body color
-			CSDL_Ext::fillSurface(shadowCornerTempl, { 0, 0, 0, 192 } );
-			CSDL_Ext::fillSurface(shadowBottomTempl, { 0, 0, 0, 192 } );
-			CSDL_Ext::fillSurface(shadowRightTempl,  { 0, 0, 0, 192 } );
-
-			//fill last row and column with more transparent color
-			blitAlphaCol(shadowRightTempl , size-1);
-			blitAlphaCol(shadowCornerTempl, size-1);
-			blitAlphaRow(shadowBottomTempl, size-1);
-			blitAlphaRow(shadowCornerTempl, size-1);
-		}
-
 		//FIXME: do something with this points
 		Point shadowStart;
 		if (options & BORDERED)
@@ -196,26 +148,36 @@ void CWindowObject::setShadow(bool on)
 		else
 			fullsize = Point(pos.w, pos.h);
 
+		Point sizeCorner(size, size);
+		Point sizeRight(fullsize.x - size, size);
+		Point sizeBottom(size, fullsize.y - size);
+
 		//create base 8x8 piece of shadow
-		SDL_Surface * shadowCorner = CSDL_Ext::copySurface(shadowCornerTempl);
-		SDL_Surface * shadowBottom = CSDL_Ext::scaleSurface(shadowBottomTempl, (fullsize.x - size), size);
-		SDL_Surface * shadowRight  = CSDL_Ext::scaleSurface(shadowRightTempl,  size, (fullsize.y - size));
+		auto imageCorner = GH.renderHandler().createImage(sizeCorner, CanvasScalingPolicy::AUTO);
+		auto imageRight  = GH.renderHandler().createImage(sizeRight,  CanvasScalingPolicy::AUTO);
+		auto imageBottom = GH.renderHandler().createImage(sizeBottom, CanvasScalingPolicy::AUTO);
+
+		Canvas canvasCorner = imageCorner->getCanvas();
+		Canvas canvasRight = imageRight->getCanvas();
+		Canvas canvasBottom = imageBottom->getCanvas();
+
+		canvasCorner.drawColor(Rect(Point(0,0), sizeCorner), { 0, 0, 0, 128 });
+		canvasRight.drawColor(Rect(Point(0,0), sizeRight), { 0, 0, 0, 128 });
+		canvasBottom.drawColor(Rect(Point(0,0), sizeBottom), { 0, 0, 0, 128 });
 
-		blitAlphaCol(shadowBottom, 0);
-		blitAlphaRow(shadowRight, 0);
+		canvasCorner.drawColor(Rect(Point(0,0), sizeCorner - Point(1,1)), { 0, 0, 0, 192 });
+		canvasRight.drawColor(Rect(Point(0,0),   sizeRight - Point(0,1)), { 0, 0, 0, 192 });
+		canvasBottom.drawColor(Rect(Point(0,0), sizeBottom - Point(1,0)), { 0, 0, 0, 192 });
 
 		//generate "shadow" object with these 3 pieces in it
 		{
 			OBJECT_CONSTRUCTION;
 
-			shadowParts.push_back(std::make_shared<CPicture>( GH.renderHandler().createImage(shadowCorner), Point(shadowPos.x,   shadowPos.y)));
-			shadowParts.push_back(std::make_shared<CPicture>( GH.renderHandler().createImage(shadowRight ),  Point(shadowPos.x,   shadowStart.y)));
-			shadowParts.push_back(std::make_shared<CPicture>( GH.renderHandler().createImage(shadowBottom), Point(shadowStart.x, shadowPos.y)));
+			shadowParts.push_back(std::make_shared<CPicture>( imageCorner, Point(shadowPos.x,   shadowPos.y)));
+			shadowParts.push_back(std::make_shared<CPicture>( imageRight, Point(shadowStart.x, shadowPos.y)));
+			shadowParts.push_back(std::make_shared<CPicture>( imageBottom,  Point(shadowPos.x,   shadowStart.y)));
 
 		}
-		SDL_FreeSurface(shadowCorner);
-		SDL_FreeSurface(shadowBottom);
-		SDL_FreeSurface(shadowRight);
 	}
 }
 

+ 1 - 1
client/windows/CWindowWithArtifacts.cpp

@@ -236,7 +236,7 @@ void CWindowWithArtifacts::setCursorAnimation(const CArtifactInstance & artInst)
 	{
 		assert(artInst.getScrollSpellID().num >= 0);
 		auto image = GH.renderHandler().loadImage(AnimationPath::builtin("spellscr"), artInst.getScrollSpellID().num, 0, EImageBlitMode::COLORKEY);
-		image->scaleTo(Point(44,34));
+		image->scaleTo(Point(44,34), EScalingAlgorithm::BILINEAR);
 
 		CCS->curh->dragAndDropCursor(image);
 	}