mirror of
https://github.com/vcmi/vcmi.git
synced 2025-03-19 21:10:12 +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 bool isTransparent(const Point & coords) 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;
|
||||
|
||||
/// Returns true if this image is still loading and can't be used
|
||||
virtual bool isLoading() const = 0;
|
||||
|
||||
virtual ~ISharedImage() = default;
|
||||
|
||||
virtual const SDL_Palette * getPalette() const = 0;
|
||||
|
@ -21,8 +21,11 @@
|
||||
#include "../render/IScreenHandler.h"
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <SDL_surface.h>
|
||||
#include <tbb/task_arena.h>
|
||||
|
||||
#include <SDL_image.h>
|
||||
#include <SDL_surface.h>
|
||||
#include <SDL_version.h>
|
||||
|
||||
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
|
||||
{
|
||||
assert(upscalingInProgress == false);
|
||||
if (!surf)
|
||||
return;
|
||||
|
||||
@ -140,6 +205,7 @@ void SDLImageShared::draw(SDL_Surface * where, SDL_Palette * palette, const Poin
|
||||
|
||||
void SDLImageShared::optimizeSurface()
|
||||
{
|
||||
assert(upscalingInProgress == false);
|
||||
if (!surf)
|
||||
return;
|
||||
|
||||
@ -155,6 +221,7 @@ void SDLImageShared::optimizeSurface()
|
||||
|
||||
std::shared_ptr<const ISharedImage> SDLImageShared::scaleInteger(int factor, SDL_Palette * palette, EImageBlitMode mode) const
|
||||
{
|
||||
assert(upscalingInProgress == false);
|
||||
if (factor <= 0)
|
||||
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)
|
||||
SDL_SetSurfacePalette(surf, palette);
|
||||
|
||||
SDLImageScaler scaler(surf, Rect(margins, fullSize));
|
||||
|
||||
// dump heuristics to differentiate tileable UI elements from map object / combat assets
|
||||
// simple heuristics to differentiate tileable UI elements from map object / combat assets
|
||||
EScalingAlgorithm algorithm;
|
||||
if (mode == EImageBlitMode::OPAQUE || mode == EImageBlitMode::COLORKEY || mode == EImageBlitMode::SIMPLE)
|
||||
scaler.scaleSurfaceIntegerFactor(GH.screenHandler().getScalingFactor(), EScalingAlgorithm::XBRZ_OPAQUE);
|
||||
algorithm = EScalingAlgorithm::XBRZ_OPAQUE;
|
||||
else
|
||||
scaler.scaleSurfaceIntegerFactor(GH.screenHandler().getScalingFactor(), EScalingAlgorithm::XBRZ_ALPHA);
|
||||
algorithm = EScalingAlgorithm::XBRZ_ALPHA;
|
||||
|
||||
SDL_Surface * scaled = scaler.acquireResultSurface();
|
||||
|
||||
auto ret = std::make_shared<SDLImageShared>(scaled);
|
||||
|
||||
ret->fullSize = scaler.getResultDimensions().dimensions();
|
||||
ret->margins = scaler.getResultDimensions().topLeft();
|
||||
|
||||
// erase our own reference
|
||||
SDL_FreeSurface(scaled);
|
||||
auto result = std::make_shared<SDLImageShared>(this, factor, algorithm);
|
||||
|
||||
if (surf->format->palette)
|
||||
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
|
||||
{
|
||||
assert(upscalingInProgress == false);
|
||||
if (palette && surf->format->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
|
||||
{
|
||||
assert(upscalingInProgress == false);
|
||||
if (!surf)
|
||||
return;
|
||||
|
||||
@ -233,6 +319,7 @@ void SDLImageShared::exportBitmap(const boost::filesystem::path& path, SDL_Palet
|
||||
|
||||
bool SDLImageShared::isTransparent(const Point & coords) const
|
||||
{
|
||||
assert(upscalingInProgress == false);
|
||||
if (surf)
|
||||
return CSDL_Ext::isTransparent(surf, coords.x - margins.x, coords.y - margins.y);
|
||||
else
|
||||
@ -241,6 +328,7 @@ bool SDLImageShared::isTransparent(const Point & coords) const
|
||||
|
||||
Rect SDLImageShared::contentRect() const
|
||||
{
|
||||
assert(upscalingInProgress == false);
|
||||
auto tmpMargins = margins;
|
||||
auto tmpSize = Point(surf->w, surf->h);
|
||||
return Rect(tmpMargins, tmpSize);
|
||||
@ -248,6 +336,7 @@ Rect SDLImageShared::contentRect() const
|
||||
|
||||
const SDL_Palette * SDLImageShared::getPalette() const
|
||||
{
|
||||
assert(upscalingInProgress == false);
|
||||
if (!surf)
|
||||
return nullptr;
|
||||
return surf->format->palette;
|
||||
@ -255,11 +344,13 @@ const SDL_Palette * SDLImageShared::getPalette() const
|
||||
|
||||
Point SDLImageShared::dimensions() const
|
||||
{
|
||||
assert(upscalingInProgress == false);
|
||||
return fullSize;
|
||||
}
|
||||
|
||||
std::shared_ptr<const ISharedImage> SDLImageShared::horizontalFlip() const
|
||||
{
|
||||
assert(upscalingInProgress == false);
|
||||
if (!surf)
|
||||
return shared_from_this();
|
||||
|
||||
@ -275,6 +366,7 @@ std::shared_ptr<const ISharedImage> SDLImageShared::horizontalFlip() const
|
||||
|
||||
std::shared_ptr<const ISharedImage> SDLImageShared::verticalFlip() const
|
||||
{
|
||||
assert(upscalingInProgress == false);
|
||||
if (!surf)
|
||||
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
|
||||
void SDLImageShared::savePalette()
|
||||
{
|
||||
assert(upscalingInProgress == false);
|
||||
// For some images that don't have palette, skip this
|
||||
if(surf->format->palette == nullptr)
|
||||
return;
|
||||
|
@ -27,14 +27,16 @@ struct SDL_Palette;
|
||||
class SDLImageShared final : public ISharedImage, public std::enable_shared_from_this<SDLImageShared>, boost::noncopyable
|
||||
{
|
||||
//Surface without empty borders
|
||||
SDL_Surface * surf;
|
||||
SDL_Surface * surf = nullptr;
|
||||
|
||||
SDL_Palette * originalPalette;
|
||||
SDL_Palette * originalPalette = nullptr;
|
||||
//size of left and top borders
|
||||
Point margins;
|
||||
//total size including borders
|
||||
Point fullSize;
|
||||
|
||||
std::atomic_bool upscalingInProgress = false;
|
||||
|
||||
// Keep the original palette, in order to do color switching operation
|
||||
void savePalette();
|
||||
|
||||
@ -47,8 +49,11 @@ public:
|
||||
SDLImageShared(const ImagePath & filename);
|
||||
//Create using existing surface, extraRef will increase refcount on SDL_Surface
|
||||
SDLImageShared(SDL_Surface * from);
|
||||
/// Creates image at specified scaling factor from source image
|
||||
SDLImageShared(const SDLImageShared * from, int integerScaleFactor, EScalingAlgorithm algorithm);
|
||||
~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 exportBitmap(const boost::filesystem::path & path, SDL_Palette * palette) const override;
|
||||
@ -56,6 +61,8 @@ public:
|
||||
bool isTransparent(const Point & coords) const override;
|
||||
Rect contentRect() const override;
|
||||
|
||||
bool isLoading() const override;
|
||||
|
||||
const SDL_Palette * getPalette() 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)
|
||||
{
|
||||
const auto & flipAndDraw = [&](FlippedImages & images, const ColorRGBA & colorMultiplier, uint8_t alphaValue){
|
||||
const auto & getFlippedImage = [&](FlippedImages & images){
|
||||
int index = 0;
|
||||
if (parameters.flipVertical)
|
||||
{
|
||||
@ -248,7 +248,12 @@ void ScalableImageShared::draw(SDL_Surface * where, const Point & dest, const Re
|
||||
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)
|
||||
@ -257,12 +262,23 @@ void ScalableImageShared::draw(SDL_Surface * where, const Point & dest, const Re
|
||||
}
|
||||
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))
|
||||
flipAndDraw(scaled.at(scalingFactor).shadow, Colors::WHITE_TRUE, parameters.alphaValue);
|
||||
|
||||
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
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user