mirror of
https://github.com/vcmi/vcmi.git
synced 2025-03-21 21:17:49 +02:00
Offloaded xbrz upscaling to background threads
This commit is contained in:
parent
391986e0ba
commit
c3fb76b56f
@ -116,8 +116,13 @@ public:
|
|||||||
virtual void exportBitmap(const boost::filesystem::path & path, SDL_Palette * palette) const = 0;
|
virtual void exportBitmap(const boost::filesystem::path & path, SDL_Palette * palette) const = 0;
|
||||||
virtual bool isTransparent(const Point & coords) const = 0;
|
virtual bool isTransparent(const Point & coords) const = 0;
|
||||||
virtual Rect contentRect() const = 0;
|
virtual Rect contentRect() const = 0;
|
||||||
|
|
||||||
|
virtual void scaledDraw(SDL_Surface * where, SDL_Palette * palette, const Point & scaling, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const = 0;
|
||||||
virtual void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const = 0;
|
virtual void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const = 0;
|
||||||
|
|
||||||
|
/// Returns true if this image is still loading and can't be used
|
||||||
|
virtual bool isLoading() const = 0;
|
||||||
|
|
||||||
virtual ~ISharedImage() = default;
|
virtual ~ISharedImage() = default;
|
||||||
|
|
||||||
virtual const SDL_Palette * getPalette() const = 0;
|
virtual const SDL_Palette * getPalette() const = 0;
|
||||||
|
@ -21,8 +21,11 @@
|
|||||||
#include "../render/IScreenHandler.h"
|
#include "../render/IScreenHandler.h"
|
||||||
|
|
||||||
#include <tbb/parallel_for.h>
|
#include <tbb/parallel_for.h>
|
||||||
#include <SDL_surface.h>
|
#include <tbb/task_arena.h>
|
||||||
|
|
||||||
#include <SDL_image.h>
|
#include <SDL_image.h>
|
||||||
|
#include <SDL_surface.h>
|
||||||
|
#include <SDL_version.h>
|
||||||
|
|
||||||
class SDLImageLoader;
|
class SDLImageLoader;
|
||||||
|
|
||||||
@ -88,8 +91,70 @@ SDLImageShared::SDLImageShared(const ImagePath & filename)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SDLImageShared::scaledDraw(SDL_Surface * where, SDL_Palette * palette, const Point & scaleTo, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const
|
||||||
|
{
|
||||||
|
assert(upscalingInProgress == false);
|
||||||
|
if (!surf)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Rect sourceRect(0, 0, surf->w, surf->h);
|
||||||
|
Point destShift(0, 0);
|
||||||
|
Point destScale = Point(surf->w, surf->h) * scaleTo / dimensions();
|
||||||
|
Point marginsScaled = margins * scaleTo / dimensions();
|
||||||
|
|
||||||
|
if(src)
|
||||||
|
{
|
||||||
|
Rect srcUnscaled(Point(src->topLeft() * dimensions() / scaleTo), Point(src->dimensions() * dimensions() / scaleTo));
|
||||||
|
|
||||||
|
if(srcUnscaled.x < margins.x)
|
||||||
|
destShift.x += marginsScaled.x - src->x;
|
||||||
|
|
||||||
|
if(srcUnscaled.y < margins.y)
|
||||||
|
destShift.y += marginsScaled.y - src->y;
|
||||||
|
|
||||||
|
sourceRect = Rect(srcUnscaled).intersect(Rect(margins.x, margins.y, surf->w, surf->h));
|
||||||
|
|
||||||
|
destScale.x = std::min(destScale.x, sourceRect.w * scaleTo.x / dimensions().x);
|
||||||
|
destScale.y = std::min(destScale.y, sourceRect.h * scaleTo.y / dimensions().y);
|
||||||
|
|
||||||
|
sourceRect -= margins;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
destShift = marginsScaled;
|
||||||
|
|
||||||
|
destShift += dest;
|
||||||
|
|
||||||
|
SDL_SetSurfaceColorMod(surf, colorMultiplier.r, colorMultiplier.g, colorMultiplier.b);
|
||||||
|
SDL_SetSurfaceAlphaMod(surf, alpha);
|
||||||
|
|
||||||
|
if (alpha != SDL_ALPHA_OPAQUE || (mode != EImageBlitMode::OPAQUE && surf->format->Amask != 0))
|
||||||
|
SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_BLEND);
|
||||||
|
else
|
||||||
|
SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_NONE);
|
||||||
|
|
||||||
|
if (palette && surf->format->palette)
|
||||||
|
SDL_SetSurfacePalette(surf, palette);
|
||||||
|
|
||||||
|
SDL_Rect srcRect = CSDL_Ext::toSDL(sourceRect);
|
||||||
|
SDL_Rect dstRect = CSDL_Ext::toSDL(Rect(destShift, destScale));
|
||||||
|
|
||||||
|
if (sourceRect.dimensions() * scaleTo / dimensions() != destScale)
|
||||||
|
logGlobal->info("???");
|
||||||
|
|
||||||
|
SDL_Surface * tempSurface = SDL_ConvertSurface(surf, where->format, 0);
|
||||||
|
int result = SDL_BlitScaled(tempSurface, &srcRect, where, &dstRect);
|
||||||
|
|
||||||
|
SDL_FreeSurface(tempSurface);
|
||||||
|
if (result != 0)
|
||||||
|
logGlobal->error("SDL_BlitScaled failed! %s", SDL_GetError());
|
||||||
|
|
||||||
|
if (surf->format->palette)
|
||||||
|
SDL_SetSurfacePalette(surf, originalPalette);
|
||||||
|
}
|
||||||
|
|
||||||
void SDLImageShared::draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const
|
void SDLImageShared::draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const
|
||||||
{
|
{
|
||||||
|
assert(upscalingInProgress == false);
|
||||||
if (!surf)
|
if (!surf)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -140,6 +205,7 @@ void SDLImageShared::draw(SDL_Surface * where, SDL_Palette * palette, const Poin
|
|||||||
|
|
||||||
void SDLImageShared::optimizeSurface()
|
void SDLImageShared::optimizeSurface()
|
||||||
{
|
{
|
||||||
|
assert(upscalingInProgress == false);
|
||||||
if (!surf)
|
if (!surf)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -155,6 +221,7 @@ void SDLImageShared::optimizeSurface()
|
|||||||
|
|
||||||
std::shared_ptr<const ISharedImage> SDLImageShared::scaleInteger(int factor, SDL_Palette * palette, EImageBlitMode mode) const
|
std::shared_ptr<const ISharedImage> SDLImageShared::scaleInteger(int factor, SDL_Palette * palette, EImageBlitMode mode) const
|
||||||
{
|
{
|
||||||
|
assert(upscalingInProgress == false);
|
||||||
if (factor <= 0)
|
if (factor <= 0)
|
||||||
throw std::runtime_error("Unable to scale by integer value of " + std::to_string(factor));
|
throw std::runtime_error("Unable to scale by integer value of " + std::to_string(factor));
|
||||||
|
|
||||||
@ -164,32 +231,50 @@ std::shared_ptr<const ISharedImage> SDLImageShared::scaleInteger(int factor, SDL
|
|||||||
if (palette && surf->format->palette)
|
if (palette && surf->format->palette)
|
||||||
SDL_SetSurfacePalette(surf, palette);
|
SDL_SetSurfacePalette(surf, palette);
|
||||||
|
|
||||||
SDLImageScaler scaler(surf, Rect(margins, fullSize));
|
// simple heuristics to differentiate tileable UI elements from map object / combat assets
|
||||||
|
EScalingAlgorithm algorithm;
|
||||||
// dump heuristics to differentiate tileable UI elements from map object / combat assets
|
|
||||||
if (mode == EImageBlitMode::OPAQUE || mode == EImageBlitMode::COLORKEY || mode == EImageBlitMode::SIMPLE)
|
if (mode == EImageBlitMode::OPAQUE || mode == EImageBlitMode::COLORKEY || mode == EImageBlitMode::SIMPLE)
|
||||||
scaler.scaleSurfaceIntegerFactor(GH.screenHandler().getScalingFactor(), EScalingAlgorithm::XBRZ_OPAQUE);
|
algorithm = EScalingAlgorithm::XBRZ_OPAQUE;
|
||||||
else
|
else
|
||||||
scaler.scaleSurfaceIntegerFactor(GH.screenHandler().getScalingFactor(), EScalingAlgorithm::XBRZ_ALPHA);
|
algorithm = EScalingAlgorithm::XBRZ_ALPHA;
|
||||||
|
|
||||||
SDL_Surface * scaled = scaler.acquireResultSurface();
|
auto result = std::make_shared<SDLImageShared>(this, factor, algorithm);
|
||||||
|
|
||||||
auto ret = std::make_shared<SDLImageShared>(scaled);
|
|
||||||
|
|
||||||
ret->fullSize = scaler.getResultDimensions().dimensions();
|
|
||||||
ret->margins = scaler.getResultDimensions().topLeft();
|
|
||||||
|
|
||||||
// erase our own reference
|
|
||||||
SDL_FreeSurface(scaled);
|
|
||||||
|
|
||||||
if (surf->format->palette)
|
if (surf->format->palette)
|
||||||
SDL_SetSurfacePalette(surf, originalPalette);
|
SDL_SetSurfacePalette(surf, originalPalette);
|
||||||
|
|
||||||
return ret;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDLImageShared::SDLImageShared(const SDLImageShared * from, int integerScaleFactor, EScalingAlgorithm algorithm)
|
||||||
|
{
|
||||||
|
static tbb::task_arena upscalingArena;
|
||||||
|
|
||||||
|
upscalingInProgress = true;
|
||||||
|
|
||||||
|
auto scaler = std::make_shared<SDLImageScaler>(from->surf, Rect(from->margins, from->fullSize));
|
||||||
|
|
||||||
|
const auto & scalingTask = [this, algorithm, scaler]()
|
||||||
|
{
|
||||||
|
scaler->scaleSurfaceIntegerFactor(GH.screenHandler().getScalingFactor(), algorithm);
|
||||||
|
surf = scaler->acquireResultSurface();
|
||||||
|
fullSize = scaler->getResultDimensions().dimensions();
|
||||||
|
margins = scaler->getResultDimensions().topLeft();
|
||||||
|
|
||||||
|
upscalingInProgress = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
upscalingArena.enqueue(scalingTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SDLImageShared::isLoading() const
|
||||||
|
{
|
||||||
|
return upscalingInProgress;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<const ISharedImage> SDLImageShared::scaleTo(const Point & size, SDL_Palette * palette) const
|
std::shared_ptr<const ISharedImage> SDLImageShared::scaleTo(const Point & size, SDL_Palette * palette) const
|
||||||
{
|
{
|
||||||
|
assert(upscalingInProgress == false);
|
||||||
if (palette && surf->format->palette)
|
if (palette && surf->format->palette)
|
||||||
SDL_SetSurfacePalette(surf, palette);
|
SDL_SetSurfacePalette(surf, palette);
|
||||||
|
|
||||||
@ -221,6 +306,7 @@ std::shared_ptr<const ISharedImage> SDLImageShared::scaleTo(const Point & size,
|
|||||||
|
|
||||||
void SDLImageShared::exportBitmap(const boost::filesystem::path& path, SDL_Palette * palette) const
|
void SDLImageShared::exportBitmap(const boost::filesystem::path& path, SDL_Palette * palette) const
|
||||||
{
|
{
|
||||||
|
assert(upscalingInProgress == false);
|
||||||
if (!surf)
|
if (!surf)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -233,6 +319,7 @@ void SDLImageShared::exportBitmap(const boost::filesystem::path& path, SDL_Palet
|
|||||||
|
|
||||||
bool SDLImageShared::isTransparent(const Point & coords) const
|
bool SDLImageShared::isTransparent(const Point & coords) const
|
||||||
{
|
{
|
||||||
|
assert(upscalingInProgress == false);
|
||||||
if (surf)
|
if (surf)
|
||||||
return CSDL_Ext::isTransparent(surf, coords.x - margins.x, coords.y - margins.y);
|
return CSDL_Ext::isTransparent(surf, coords.x - margins.x, coords.y - margins.y);
|
||||||
else
|
else
|
||||||
@ -241,6 +328,7 @@ bool SDLImageShared::isTransparent(const Point & coords) const
|
|||||||
|
|
||||||
Rect SDLImageShared::contentRect() const
|
Rect SDLImageShared::contentRect() const
|
||||||
{
|
{
|
||||||
|
assert(upscalingInProgress == false);
|
||||||
auto tmpMargins = margins;
|
auto tmpMargins = margins;
|
||||||
auto tmpSize = Point(surf->w, surf->h);
|
auto tmpSize = Point(surf->w, surf->h);
|
||||||
return Rect(tmpMargins, tmpSize);
|
return Rect(tmpMargins, tmpSize);
|
||||||
@ -248,6 +336,7 @@ Rect SDLImageShared::contentRect() const
|
|||||||
|
|
||||||
const SDL_Palette * SDLImageShared::getPalette() const
|
const SDL_Palette * SDLImageShared::getPalette() const
|
||||||
{
|
{
|
||||||
|
assert(upscalingInProgress == false);
|
||||||
if (!surf)
|
if (!surf)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return surf->format->palette;
|
return surf->format->palette;
|
||||||
@ -255,11 +344,13 @@ const SDL_Palette * SDLImageShared::getPalette() const
|
|||||||
|
|
||||||
Point SDLImageShared::dimensions() const
|
Point SDLImageShared::dimensions() const
|
||||||
{
|
{
|
||||||
|
assert(upscalingInProgress == false);
|
||||||
return fullSize;
|
return fullSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<const ISharedImage> SDLImageShared::horizontalFlip() const
|
std::shared_ptr<const ISharedImage> SDLImageShared::horizontalFlip() const
|
||||||
{
|
{
|
||||||
|
assert(upscalingInProgress == false);
|
||||||
if (!surf)
|
if (!surf)
|
||||||
return shared_from_this();
|
return shared_from_this();
|
||||||
|
|
||||||
@ -275,6 +366,7 @@ std::shared_ptr<const ISharedImage> SDLImageShared::horizontalFlip() const
|
|||||||
|
|
||||||
std::shared_ptr<const ISharedImage> SDLImageShared::verticalFlip() const
|
std::shared_ptr<const ISharedImage> SDLImageShared::verticalFlip() const
|
||||||
{
|
{
|
||||||
|
assert(upscalingInProgress == false);
|
||||||
if (!surf)
|
if (!surf)
|
||||||
return shared_from_this();
|
return shared_from_this();
|
||||||
|
|
||||||
@ -291,6 +383,7 @@ std::shared_ptr<const ISharedImage> SDLImageShared::verticalFlip() const
|
|||||||
// Keep the original palette, in order to do color switching operation
|
// Keep the original palette, in order to do color switching operation
|
||||||
void SDLImageShared::savePalette()
|
void SDLImageShared::savePalette()
|
||||||
{
|
{
|
||||||
|
assert(upscalingInProgress == false);
|
||||||
// For some images that don't have palette, skip this
|
// For some images that don't have palette, skip this
|
||||||
if(surf->format->palette == nullptr)
|
if(surf->format->palette == nullptr)
|
||||||
return;
|
return;
|
||||||
|
@ -27,14 +27,16 @@ struct SDL_Palette;
|
|||||||
class SDLImageShared final : public ISharedImage, public std::enable_shared_from_this<SDLImageShared>, boost::noncopyable
|
class SDLImageShared final : public ISharedImage, public std::enable_shared_from_this<SDLImageShared>, boost::noncopyable
|
||||||
{
|
{
|
||||||
//Surface without empty borders
|
//Surface without empty borders
|
||||||
SDL_Surface * surf;
|
SDL_Surface * surf = nullptr;
|
||||||
|
|
||||||
SDL_Palette * originalPalette;
|
SDL_Palette * originalPalette = nullptr;
|
||||||
//size of left and top borders
|
//size of left and top borders
|
||||||
Point margins;
|
Point margins;
|
||||||
//total size including borders
|
//total size including borders
|
||||||
Point fullSize;
|
Point fullSize;
|
||||||
|
|
||||||
|
std::atomic_bool upscalingInProgress = false;
|
||||||
|
|
||||||
// Keep the original palette, in order to do color switching operation
|
// Keep the original palette, in order to do color switching operation
|
||||||
void savePalette();
|
void savePalette();
|
||||||
|
|
||||||
@ -47,8 +49,11 @@ public:
|
|||||||
SDLImageShared(const ImagePath & filename);
|
SDLImageShared(const ImagePath & filename);
|
||||||
//Create using existing surface, extraRef will increase refcount on SDL_Surface
|
//Create using existing surface, extraRef will increase refcount on SDL_Surface
|
||||||
SDLImageShared(SDL_Surface * from);
|
SDLImageShared(SDL_Surface * from);
|
||||||
|
/// Creates image at specified scaling factor from source image
|
||||||
|
SDLImageShared(const SDLImageShared * from, int integerScaleFactor, EScalingAlgorithm algorithm);
|
||||||
~SDLImageShared();
|
~SDLImageShared();
|
||||||
|
|
||||||
|
void scaledDraw(SDL_Surface * where, SDL_Palette * palette, const Point & scaling, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const override;
|
||||||
void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const override;
|
void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const override;
|
||||||
|
|
||||||
void exportBitmap(const boost::filesystem::path & path, SDL_Palette * palette) const override;
|
void exportBitmap(const boost::filesystem::path & path, SDL_Palette * palette) const override;
|
||||||
@ -56,6 +61,8 @@ public:
|
|||||||
bool isTransparent(const Point & coords) const override;
|
bool isTransparent(const Point & coords) const override;
|
||||||
Rect contentRect() const override;
|
Rect contentRect() const override;
|
||||||
|
|
||||||
|
bool isLoading() const override;
|
||||||
|
|
||||||
const SDL_Palette * getPalette() const override;
|
const SDL_Palette * getPalette() const override;
|
||||||
|
|
||||||
[[nodiscard]] std::shared_ptr<const ISharedImage> horizontalFlip() const override;
|
[[nodiscard]] std::shared_ptr<const ISharedImage> horizontalFlip() const override;
|
||||||
|
@ -230,7 +230,7 @@ Rect ScalableImageShared::contentRect() const
|
|||||||
|
|
||||||
void ScalableImageShared::draw(SDL_Surface * where, const Point & dest, const Rect * src, const ScalableImageParameters & parameters, int scalingFactor)
|
void ScalableImageShared::draw(SDL_Surface * where, const Point & dest, const Rect * src, const ScalableImageParameters & parameters, int scalingFactor)
|
||||||
{
|
{
|
||||||
const auto & flipAndDraw = [&](FlippedImages & images, const ColorRGBA & colorMultiplier, uint8_t alphaValue){
|
const auto & getFlippedImage = [&](FlippedImages & images){
|
||||||
int index = 0;
|
int index = 0;
|
||||||
if (parameters.flipVertical)
|
if (parameters.flipVertical)
|
||||||
{
|
{
|
||||||
@ -248,7 +248,12 @@ void ScalableImageShared::draw(SDL_Surface * where, const Point & dest, const Re
|
|||||||
index |= 2;
|
index |= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
images[index]->draw(where, parameters.palette, dest, src, colorMultiplier, alphaValue, locator.layer);
|
return images[index];
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto & flipAndDraw = [&](FlippedImages & images, const ColorRGBA & colorMultiplier, uint8_t alphaValue){
|
||||||
|
|
||||||
|
getFlippedImage(images)->draw(where, parameters.palette, dest, src, colorMultiplier, alphaValue, locator.layer);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (scalingFactor == 1)
|
if (scalingFactor == 1)
|
||||||
@ -257,12 +262,23 @@ void ScalableImageShared::draw(SDL_Surface * where, const Point & dest, const Re
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
bool shadowLoading = scaled.at(scalingFactor).shadow.at(0) && scaled.at(scalingFactor).shadow.at(0)->isLoading();
|
||||||
|
bool bodyLoading = scaled.at(scalingFactor).body.at(0) && scaled.at(scalingFactor).body.at(0)->isLoading();
|
||||||
|
bool overlayLoading = scaled.at(scalingFactor).overlay.at(0) && scaled.at(scalingFactor).overlay.at(0)->isLoading();
|
||||||
|
bool playerLoading = parameters.player.isValidPlayer() && scaled.at(scalingFactor).playerColored.at(parameters.player.getNum()) && scaled.at(scalingFactor).playerColored.at(parameters.player.getNum())->isLoading();
|
||||||
|
|
||||||
|
if (shadowLoading || bodyLoading || overlayLoading || playerLoading)
|
||||||
|
{
|
||||||
|
getFlippedImage(base)->scaledDraw(where, parameters.palette, dimensions() * scalingFactor, dest, src, parameters.colorMultiplier, parameters.alphaValue, locator.layer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (scaled.at(scalingFactor).shadow.at(0))
|
if (scaled.at(scalingFactor).shadow.at(0))
|
||||||
flipAndDraw(scaled.at(scalingFactor).shadow, Colors::WHITE_TRUE, parameters.alphaValue);
|
flipAndDraw(scaled.at(scalingFactor).shadow, Colors::WHITE_TRUE, parameters.alphaValue);
|
||||||
|
|
||||||
if (parameters.player.isValidPlayer())
|
if (parameters.player.isValidPlayer())
|
||||||
{
|
{
|
||||||
scaled.at(scalingFactor).playerColored[parameters.player.getNum()]->draw(where, parameters.palette, dest, src, Colors::WHITE_TRUE, parameters.alphaValue, locator.layer);
|
scaled.at(scalingFactor).playerColored.at(parameters.player.getNum())->draw(where, parameters.palette, dest, src, Colors::WHITE_TRUE, parameters.alphaValue, locator.layer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user