1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-21 17:17:06 +02:00

First iteration of upscaled rendering

This commit is contained in:
Ivan Savenko 2024-07-21 21:11:02 +00:00
parent 7f5cd8a7aa
commit 4171026035
13 changed files with 294 additions and 26 deletions

View File

@ -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

View File

@ -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()

View File

@ -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));
}

View File

@ -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();

View File

@ -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;
};

View File

@ -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)
{
}

View File

@ -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;
};

View File

@ -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));
}
}

View File

@ -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

View File

@ -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()

View File

@ -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);

View File

@ -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;

View File

@ -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;