Browse Source

First iteration of upscaled rendering

Ivan Savenko 1 year ago
parent
commit
4171026035

+ 2 - 0
client/CMakeLists.txt

@@ -97,6 +97,7 @@ set(client_SRCS
 	renderSDL/CTrueTypeFont.cpp
 	renderSDL/CursorHardware.cpp
 	renderSDL/CursorSoftware.cpp
+	renderSDL/ImageScaled.cpp
 	renderSDL/RenderHandler.cpp
 	renderSDL/SDLImage.cpp
 	renderSDL/SDLImageLoader.cpp
@@ -301,6 +302,7 @@ set(client_HEADERS
 	renderSDL/CTrueTypeFont.h
 	renderSDL/CursorHardware.h
 	renderSDL/CursorSoftware.h
+	renderSDL/ImageScaled.h
 	renderSDL/RenderHandler.h
 	renderSDL/SDLImage.h
 	renderSDL/SDLImageLoader.h

+ 1 - 1
client/gui/CGuiHandler.cpp

@@ -176,7 +176,7 @@ const Point & CGuiHandler::getCursorPosition() const
 
 Point CGuiHandler::screenDimensions() const
 {
-	return Point(screen->w, screen->h);
+	return screenHandlerInstance->getLogicalResolution();
 }
 
 void CGuiHandler::drawFPSCounter()

+ 38 - 21
client/render/Canvas.cpp

@@ -47,13 +47,28 @@ Canvas::Canvas(const Canvas & other, const Rect & newClipRect):
 }
 
 Canvas::Canvas(const Point & size):
-	renderArea(Point(0,0), size),
-	surface(CSDL_Ext::newSurface(size))
+	renderArea(Point(0,0), size * getScalingFactor()),
+	surface(CSDL_Ext::newSurface(size * getScalingFactor()))
 {
 	CSDL_Ext::fillSurface(surface, CSDL_Ext::toSDL(Colors::TRANSPARENCY) );
 	SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE);
 }
 
+int Canvas::getScalingFactor() const
+{
+	return 2; // TODO: get from screen handler
+}
+
+Point Canvas::transformPos(const Point & input)
+{
+	return (renderArea.topLeft() + input) * getScalingFactor();
+}
+
+Point Canvas::transformSize(const Point & input)
+{
+	return input * getScalingFactor();
+}
+
 Canvas Canvas::createFromSurface(SDL_Surface * surface)
 {
 	return Canvas(surface);
@@ -81,19 +96,20 @@ void Canvas::draw(const std::shared_ptr<IImage>& image, const Point & pos)
 {
 	assert(image);
 	if (image)
-		image->draw(surface, pos + renderArea.topLeft());
+		image->draw(surface, transformPos(pos));
 }
 
 void Canvas::draw(const std::shared_ptr<IImage>& image, const Point & pos, const Rect & sourceRect)
 {
+	Rect realSourceRect = sourceRect * getScalingFactor();
 	assert(image);
 	if (image)
-		image->draw(surface, pos + renderArea.topLeft(), &sourceRect);
+		image->draw(surface, transformPos(pos), &realSourceRect);
 }
 
 void Canvas::draw(const Canvas & image, const Point & pos)
 {
-	CSDL_Ext::blitSurface(image.surface, image.renderArea, surface, renderArea.topLeft() + pos);
+	CSDL_Ext::blitSurface(image.surface, image.renderArea, surface, transformPos(pos));
 }
 
 void Canvas::drawTransparent(const Canvas & image, const Point & pos, double transparency)
@@ -103,35 +119,36 @@ void Canvas::drawTransparent(const Canvas & image, const Point & pos, double tra
 	SDL_GetSurfaceBlendMode(image.surface, &oldMode);
 	SDL_SetSurfaceBlendMode(image.surface, SDL_BLENDMODE_BLEND);
 	SDL_SetSurfaceAlphaMod(image.surface, 255 * transparency);
-	CSDL_Ext::blitSurface(image.surface, image.renderArea, surface, renderArea.topLeft() + pos);
+	CSDL_Ext::blitSurface(image.surface, image.renderArea, surface, transformPos(pos));
 	SDL_SetSurfaceAlphaMod(image.surface, 255);
 	SDL_SetSurfaceBlendMode(image.surface, oldMode);
 }
 
 void Canvas::drawScaled(const Canvas & image, const Point & pos, const Point & targetSize)
 {
-	SDL_Rect targetRect = CSDL_Ext::toSDL(Rect(pos + renderArea.topLeft(), targetSize));
+	SDL_Rect targetRect = CSDL_Ext::toSDL(Rect(transformPos(pos), transformSize(targetSize)));
 	SDL_BlitScaled(image.surface, nullptr, surface, &targetRect);
 }
 
 void Canvas::drawPoint(const Point & dest, const ColorRGBA & color)
 {
-	CSDL_Ext::putPixelWithoutRefreshIfInSurf(surface, dest.x, dest.y, color.r, color.g, color.b, color.a);
+	Point point = transformPos(dest);
+	CSDL_Ext::putPixelWithoutRefreshIfInSurf(surface, point.x, point.y, color.r, color.g, color.b, color.a);
 }
 
 void Canvas::drawLine(const Point & from, const Point & dest, const ColorRGBA & colorFrom, const ColorRGBA & colorDest)
 {
-	CSDL_Ext::drawLine(surface, renderArea.topLeft() + from, renderArea.topLeft() + dest, CSDL_Ext::toSDL(colorFrom), CSDL_Ext::toSDL(colorDest));
+	CSDL_Ext::drawLine(surface, transformPos(from), transformPos(dest), CSDL_Ext::toSDL(colorFrom), CSDL_Ext::toSDL(colorDest));
 }
 
 void Canvas::drawLineDashed(const Point & from, const Point & dest, const ColorRGBA & color)
 {
-	CSDL_Ext::drawLineDashed(surface, renderArea.topLeft() + from, renderArea.topLeft() + dest, CSDL_Ext::toSDL(color));
+	CSDL_Ext::drawLineDashed(surface, transformPos(from), transformPos(dest), CSDL_Ext::toSDL(color));
 }
 
 void Canvas::drawBorder(const Rect & target, const ColorRGBA & color, int width)
 {
-	Rect realTarget = target + renderArea.topLeft();
+	Rect realTarget = (target + renderArea.topLeft()) * getScalingFactor();
 
 	CSDL_Ext::drawBorder(surface, realTarget.x, realTarget.y, realTarget.w, realTarget.h, CSDL_Ext::toSDL(color), width);
 }
@@ -150,10 +167,10 @@ void Canvas::drawText(const Point & position, const EFonts & font, const ColorRG
 {
 	switch (alignment)
 	{
-	case ETextAlignment::TOPLEFT:      return graphics->fonts[font]->renderTextLeft  (surface, text, colorDest, renderArea.topLeft() + position);
-	case ETextAlignment::TOPCENTER:    return graphics->fonts[font]->renderTextCenter(surface, text, colorDest, renderArea.topLeft() + position);
-	case ETextAlignment::CENTER:       return graphics->fonts[font]->renderTextCenter(surface, text, colorDest, renderArea.topLeft() + position);
-	case ETextAlignment::BOTTOMRIGHT:  return graphics->fonts[font]->renderTextRight (surface, text, colorDest, renderArea.topLeft() + position);
+	case ETextAlignment::TOPLEFT:      return graphics->fonts[font]->renderTextLeft  (surface, text, colorDest, transformPos(position));
+	case ETextAlignment::TOPCENTER:    return graphics->fonts[font]->renderTextCenter(surface, text, colorDest, transformPos(position));
+	case ETextAlignment::CENTER:       return graphics->fonts[font]->renderTextCenter(surface, text, colorDest, transformPos(position));
+	case ETextAlignment::BOTTOMRIGHT:  return graphics->fonts[font]->renderTextRight (surface, text, colorDest, transformPos(position));
 	}
 }
 
@@ -161,23 +178,23 @@ void Canvas::drawText(const Point & position, const EFonts & font, const ColorRG
 {
 	switch (alignment)
 	{
-	case ETextAlignment::TOPLEFT:      return graphics->fonts[font]->renderTextLinesLeft  (surface, text, colorDest, renderArea.topLeft() + position);
-	case ETextAlignment::TOPCENTER:    return graphics->fonts[font]->renderTextLinesCenter(surface, text, colorDest, renderArea.topLeft() + position);
-	case ETextAlignment::CENTER:       return graphics->fonts[font]->renderTextLinesCenter(surface, text, colorDest, renderArea.topLeft() + position);
-	case ETextAlignment::BOTTOMRIGHT:  return graphics->fonts[font]->renderTextLinesRight (surface, text, colorDest, renderArea.topLeft() + position);
+	case ETextAlignment::TOPLEFT:      return graphics->fonts[font]->renderTextLinesLeft  (surface, text, colorDest, transformPos(position));
+	case ETextAlignment::TOPCENTER:    return graphics->fonts[font]->renderTextLinesCenter(surface, text, colorDest, transformPos(position));
+	case ETextAlignment::CENTER:       return graphics->fonts[font]->renderTextLinesCenter(surface, text, colorDest, transformPos(position));
+	case ETextAlignment::BOTTOMRIGHT:  return graphics->fonts[font]->renderTextLinesRight (surface, text, colorDest, transformPos(position));
 	}
 }
 
 void Canvas::drawColor(const Rect & target, const ColorRGBA & color)
 {
-	Rect realTarget = target + renderArea.topLeft();
+	Rect realTarget = (target + renderArea.topLeft()) * getScalingFactor();
 
 	CSDL_Ext::fillRect(surface, realTarget, CSDL_Ext::toSDL(color));
 }
 
 void Canvas::drawColorBlended(const Rect & target, const ColorRGBA & color)
 {
-	Rect realTarget = target + renderArea.topLeft();
+	Rect realTarget = (target + renderArea.topLeft()) * getScalingFactor();
 
 	CSDL_Ext::fillRectBlended(surface, realTarget, CSDL_Ext::toSDL(color));
 }

+ 5 - 0
client/render/Canvas.h

@@ -32,6 +32,9 @@ class Canvas
 	/// copy constructor
 	Canvas(const Canvas & other);
 
+	Point transformPos(const Point & input);
+	Point transformSize(const Point & input);
+
 public:
 	Canvas & operator = (const Canvas & other) = delete;
 	Canvas & operator = (Canvas && other) = delete;
@@ -102,6 +105,8 @@ public:
 	/// fills canvas with texture
 	void fillTexture(const std::shared_ptr<IImage>& image);
 
+	int getScalingFactor() const;
+
 	/// Compatibility method. AVOID USAGE. To be removed once SDL abstraction layer is finished.
 	SDL_Surface * getInternalSurface();
 

+ 3 - 0
client/render/IScreenHandler.h

@@ -41,6 +41,9 @@ public:
 	/// Dimensions of render output
 	virtual Point getRenderResolution() const = 0;
 
+	/// Dimensions of logical output. Can be different if scaling is used
+	virtual Point getLogicalResolution() const = 0;
+
 	/// Window has focus
 	virtual bool hasFocus() = 0;
 };

+ 133 - 0
client/renderSDL/ImageScaled.cpp

@@ -0,0 +1,133 @@
+/*
+ * ImageScaled.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+#include "ImageScaled.h"
+
+#include "SDLImage.h"
+#include "SDL_Extensions.h"
+
+#include "../../lib/constants/EntityIdentifiers.h"
+
+#include <SDL_surface.h>
+
+ImageConstScaled::ImageConstScaled(std::shared_ptr<SDLImageConst> sourceImage)
+	:sourceImage(sourceImage)
+{
+	scaledImage = sourceImage->scaleFast(sourceImage->dimensions() * getScalingFactor());
+}
+
+int ImageConstScaled::getScalingFactor() const
+{
+	return 2;
+}
+
+void ImageConstScaled::draw(SDL_Surface *where, const Point &dest, const Rect *src, uint8_t alpha, EImageBlitMode mode) const
+{
+	scaledImage->draw(where, nullptr, dest, src, alpha, mode);
+}
+
+void ImageConstScaled::exportBitmap(const boost::filesystem::path &path) const
+{
+	sourceImage->exportBitmap(path);
+}
+
+Point ImageConstScaled::dimensions() const
+{
+	return sourceImage->dimensions();
+}
+
+bool ImageConstScaled::isTransparent(const Point &coords) const
+{
+	return sourceImage->isTransparent(coords);
+}
+
+std::shared_ptr<IImage> ImageConstScaled::createImageReference(EImageBlitMode mode)
+{
+	return std::make_shared<ImageScaled>(shared_from_this(), mode);
+}
+
+std::shared_ptr<IConstImage> ImageConstScaled::horizontalFlip() const
+{
+	return std::make_shared<ImageConstScaled>(std::dynamic_pointer_cast<SDLImageConst>(sourceImage->horizontalFlip()));
+}
+
+std::shared_ptr<IConstImage> ImageConstScaled::verticalFlip() const
+{
+	return std::make_shared<ImageConstScaled>(std::dynamic_pointer_cast<SDLImageConst>(sourceImage->verticalFlip()));
+}
+
+std::shared_ptr<ImageConstScaled> ImageConstScaled::scaleFast(const Point &size) const
+{
+	return std::make_shared<ImageConstScaled>(sourceImage->scaleFast(size));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+ImageScaled::ImageScaled(const std::shared_ptr<ImageConstScaled> &image, EImageBlitMode mode)
+	:image(image)
+	, alphaValue(SDL_ALPHA_OPAQUE)
+	, blitMode(mode)
+{
+}
+
+void ImageScaled::scaleFast(const Point &size)
+{
+	image = image->scaleFast(size);
+}
+
+void ImageScaled::exportBitmap(const boost::filesystem::path &path) const
+{
+	image->exportBitmap(path);
+}
+
+bool ImageScaled::isTransparent(const Point &coords) const
+{
+	return image->isTransparent(coords);
+}
+
+Point ImageScaled::dimensions() const
+{
+	return image->dimensions();
+}
+
+void ImageScaled::setAlpha(uint8_t value)
+{
+	alphaValue = value;
+}
+
+void ImageScaled::setBlitMode(EImageBlitMode mode)
+{
+	blitMode = mode;
+}
+
+void ImageScaled::draw(SDL_Surface *where, const Point &pos, const Rect *src) const
+{
+	image->draw(where, pos, src, alphaValue, blitMode);
+}
+
+void ImageScaled::setSpecialPalette(const SpecialPalette &SpecialPalette, uint32_t colorsToSkipMask)
+{
+}
+
+void ImageScaled::playerColored(PlayerColor player)
+{
+}
+
+void ImageScaled::setFlagColor(PlayerColor player)
+{
+}
+
+void ImageScaled::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove)
+{
+}
+
+void ImageScaled::adjustPalette(const ColorFilter &shifter, uint32_t colorsToSkipMask)
+{
+}

+ 61 - 0
client/renderSDL/ImageScaled.h

@@ -0,0 +1,61 @@
+/*
+ * ImageScaled.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "../render/IImage.h"
+
+struct SDL_Palette;
+
+class SDLImageConst;
+
+class ImageConstScaled final : public IConstImage, public std::enable_shared_from_this<ImageConstScaled>, boost::noncopyable
+{
+	std::shared_ptr<SDLImageConst> sourceImage;
+	std::shared_ptr<SDLImageConst> scaledImage;
+
+	int getScalingFactor() const;
+public:
+	ImageConstScaled(std::shared_ptr<SDLImageConst> sourceImage);
+
+	void draw(SDL_Surface * where, const Point & dest, const Rect * src, uint8_t alpha, EImageBlitMode mode) const;
+
+	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<IConstImage> horizontalFlip() const override;
+	std::shared_ptr<IConstImage> verticalFlip() const override;
+	std::shared_ptr<ImageConstScaled> scaleFast(const Point & size) const;
+};
+
+class ImageScaled final : public IImage
+{
+private:
+	std::shared_ptr<ImageConstScaled> image;
+
+	uint8_t alphaValue;
+	EImageBlitMode blitMode;
+
+public:
+	ImageScaled(const std::shared_ptr<ImageConstScaled> & image, EImageBlitMode mode);
+
+	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 draw(SDL_Surface * where, const Point & pos, const Rect * src) const override;
+	void setSpecialPalette(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;
+};

+ 16 - 2
client/renderSDL/RenderHandler.cpp

@@ -11,6 +11,7 @@
 #include "RenderHandler.h"
 
 #include "SDLImage.h"
+#include "ImageScaled.h"
 
 #include "../render/CAnimation.h"
 #include "../render/CDefFile.h"
@@ -125,9 +126,22 @@ RenderHandler::AnimationLayoutMap & RenderHandler::getAnimationLayout(const Anim
 	return animationLayouts[actualPath];
 }
 
+int RenderHandler::getScalingFactor() const
+{
+	return 2;
+}
+
+std::shared_ptr<IConstImage> RenderHandler::createScaledImage(std::shared_ptr<SDLImageConst> input)
+{
+	if (getScalingFactor() == 1)
+		return input;
+
+	return std::make_shared<ImageConstScaled>(input);
+}
+
 std::shared_ptr<IConstImage> RenderHandler::loadImageFromSingleFile(const ImagePath & path)
 {
-	auto result = std::make_shared<SDLImageConst>(path);
+	auto result = createScaledImage(std::make_shared<SDLImageConst>(path));
 	imageFiles[ImageLocator(path)] = result;
 	return result;
 }
@@ -149,7 +163,7 @@ std::shared_ptr<IConstImage> RenderHandler::loadImageFromAnimationFileUncached(c
 	else
 	{
 		auto defFile = getAnimationFile(path);
-		return std::make_shared<SDLImageConst>(defFile.get(), frame, group);
+		return createScaledImage(std::make_shared<SDLImageConst>(defFile.get(), frame, group));
 	}
 }
 

+ 5 - 0
client/renderSDL/RenderHandler.h

@@ -16,6 +16,7 @@ class EntityService;
 VCMI_LIB_NAMESPACE_END
 
 class CDefFile;
+class SDLImageConst;
 class IConstImage;
 
 class RenderHandler : public IRenderHandler
@@ -37,6 +38,10 @@ class RenderHandler : public IRenderHandler
 	std::shared_ptr<IConstImage> loadImageFromAnimationFileUncached(const AnimationPath & path, int frame, int group);
 	std::shared_ptr<IConstImage> loadImageFromAnimationFile(const AnimationPath & path, int frame, int group);
 	std::shared_ptr<IConstImage> loadImageImpl(const ImageLocator & config);
+
+	int getScalingFactor() const;
+
+	std::shared_ptr<IConstImage> createScaledImage(std::shared_ptr<SDLImageConst> input);
 public:
 
 	// IRenderHandler implementation

+ 6 - 1
client/renderSDL/SDL_Extensions.h

@@ -110,11 +110,16 @@ using TColorPutterAlpha = void (*)(uint8_t *&, const uint8_t &, const uint8_t &,
 		SDL_Surface * surf;
 		Rect oldRect;
 
+		int getScalingFactor() const
+		{
+			return 2;
+		}
+
 	public:
 		CClipRectGuard(SDL_Surface * surface, const Rect & rect): surf(surface)
 		{
 			CSDL_Ext::getClipRect(surf, oldRect);
-			CSDL_Ext::setClipRect(surf, rect);
+			CSDL_Ext::setClipRect(surf, rect * getScalingFactor());
 		}
 
 		~CClipRectGuard()

+ 13 - 1
client/renderSDL/ScreenHandler.cpp

@@ -41,8 +41,10 @@ static const std::string NAME = GameConstants::VCMI_VERSION; //application name
 
 std::tuple<int, int> ScreenHandler::getSupportedScalingRange() const
 {
+	// Renderer upscaling factor. TODO: make configurable
+	static const int scalingFactor = 2;
 	// H3 resolution, any resolution smaller than that is not correctly supported
-	static const Point minResolution = {800, 600};
+	static const Point minResolution = Point(800, 600) * scalingFactor;
 	// arbitrary limit on *downscaling*. Allow some downscaling, if requested by user. Should be generally limited to 100+ for all but few devices
 	static const double minimalScaling = 50;
 
@@ -99,6 +101,16 @@ Point ScreenHandler::getPreferredLogicalResolution() const
 	return logicalResolution;
 }
 
+int ScreenHandler::getScalingFactor() const
+{
+	return 2;
+}
+
+Point ScreenHandler::getLogicalResolution() const
+{
+	return Point(screen->w, screen->h) / getScalingFactor();
+}
+
 Point ScreenHandler::getRenderResolution() const
 {
 	assert(mainRenderer != nullptr);

+ 4 - 0
client/renderSDL/ScreenHandler.h

@@ -69,6 +69,8 @@ class ScreenHandler final : public IScreenHandler
 
 	/// Performs validation of settings and updates them to valid values if necessary
 	void validateSettings();
+
+	int getScalingFactor() const;
 public:
 
 	/// Creates and initializes screen, window and SDL state
@@ -89,6 +91,8 @@ public:
 	/// Window has focus
 	bool hasFocus() final;
 
+	Point getLogicalResolution() const final;
+
 	std::vector<Point> getSupportedResolutions() const final;
 	std::vector<Point> getSupportedResolutions(int displayIndex) const;
 	std::tuple<int, int> getSupportedScalingRange() const final;

+ 7 - 0
lib/Rect.h

@@ -119,6 +119,13 @@ public:
 		return Rect(x-p.x,y-p.y,w,h);
 	}
 
+	template<typename T>
+	Rect operator*(const T &mul) const
+	{
+		return Rect(x*mul, y*mul, w*mul, h*mul);
+	}
+
+
 	Rect& operator=(const Rect &p)
 	{
 		x = p.x;