diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 9a9051c7f..f1679d045 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -104,6 +104,7 @@ set(vcmiclientcommon_SRCS renderSDL/RenderHandler.cpp renderSDL/SDLImage.cpp renderSDL/SDLImageLoader.cpp + renderSDL/SDLImageScaler.cpp renderSDL/SDLRWwrapper.cpp renderSDL/ScreenHandler.cpp renderSDL/SDL_Extensions.cpp @@ -313,6 +314,7 @@ set(vcmiclientcommon_HEADERS renderSDL/RenderHandler.h renderSDL/SDLImage.h renderSDL/SDLImageLoader.h + renderSDL/SDLImageScaler.h renderSDL/SDLRWwrapper.h renderSDL/ScreenHandler.h renderSDL/SDL_Extensions.h diff --git a/client/render/CanvasImage.cpp b/client/render/CanvasImage.cpp index d16a30a14..07e9b3474 100644 --- a/client/render/CanvasImage.cpp +++ b/client/render/CanvasImage.cpp @@ -13,6 +13,7 @@ #include "../gui/CGuiHandler.h" #include "../render/IScreenHandler.h" #include "../renderSDL/SDL_Extensions.h" +#include "../renderSDL/SDLImageScaler.h" #include #include @@ -35,9 +36,10 @@ void CanvasImage::scaleTo(const Point & size, EScalingAlgorithm algorithm) { Point scaledSize = size * GH.screenHandler().getScalingFactor(); - auto newSurface = CSDL_Ext::scaleSurface(surface, scaledSize.x, scaledSize.y, algorithm); + SDLImageScaler scaler(surface); + scaler.scaleSurface(scaledSize, algorithm); SDL_FreeSurface(surface); - surface = newSurface; + surface = scaler.acquireResultSurface(); } void CanvasImage::exportBitmap(const boost::filesystem::path & path) const diff --git a/client/renderSDL/CBitmapFont.cpp b/client/renderSDL/CBitmapFont.cpp index aa36e6c88..13cdfb344 100644 --- a/client/renderSDL/CBitmapFont.cpp +++ b/client/renderSDL/CBitmapFont.cpp @@ -11,6 +11,8 @@ #include "CBitmapFont.h" #include "SDL_Extensions.h" +#include "SDLImageScaler.h" + #include "../CGameInfo.h" #include "../gui/CGuiHandler.h" #include "../render/Colors.h" @@ -207,9 +209,10 @@ CBitmapFont::CBitmapFont(const std::string & filename): auto filterName = settings["video"]["fontUpscalingFilter"].String(); EScalingAlgorithm algorithm = filterNameToEnum.at(filterName); - auto scaledSurface = CSDL_Ext::scaleSurfaceIntegerFactor(atlasImage, GH.screenHandler().getScalingFactor(), algorithm); + SDLImageScaler scaler(atlasImage); + scaler.scaleSurfaceIntegerFactor(GH.screenHandler().getScalingFactor(), algorithm); SDL_FreeSurface(atlasImage); - atlasImage = scaledSurface; + atlasImage = scaler.acquireResultSurface(); } logGlobal->debug("Loaded BMP font: '%s', height %d, ascent %d", diff --git a/client/renderSDL/CursorHardware.cpp b/client/renderSDL/CursorHardware.cpp index 4267a7710..569ce7399 100644 --- a/client/renderSDL/CursorHardware.cpp +++ b/client/renderSDL/CursorHardware.cpp @@ -12,6 +12,7 @@ #include "CursorHardware.h" #include "SDL_Extensions.h" +#include "SDLImageScaler.h" #include "../gui/CGuiHandler.h" #include "../render/IScreenHandler.h" @@ -60,7 +61,10 @@ void CursorHardware::setImage(std::shared_ptr 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, EScalingAlgorithm::BILINEAR ); + + SDLImageScaler scaler(cursorSurface); + scaler.scaleSurface(cursorDimensionsScaled, EScalingAlgorithm::BILINEAR); + SDL_Surface * cursorSurfaceScaled = scaler.acquireResultSurface(); auto oldCursor = cursor; cursor = SDL_CreateColorCursor(cursorSurfaceScaled, pivotOffsetScaled.x, pivotOffsetScaled.y); diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index cc1c43fba..2812e1696 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -248,10 +248,11 @@ std::shared_ptr RenderHandler::loadScaledImage(const ImageLocato pathToLoad = *remappedLocator.image; } - if(!locator.image) - return nullptr; + if(locator.image) + pathToLoad = *locator.image; - pathToLoad = *locator.image; + if (pathToLoad.empty()) + return nullptr; std::string imagePathString = pathToLoad.getName(); diff --git a/client/renderSDL/SDLImage.cpp b/client/renderSDL/SDLImage.cpp index 1d1d87715..97175271b 100644 --- a/client/renderSDL/SDLImage.cpp +++ b/client/renderSDL/SDLImage.cpp @@ -11,14 +11,12 @@ #include "SDLImage.h" #include "SDLImageLoader.h" +#include "SDLImageScaler.h" #include "SDL_Extensions.h" #include "../render/ColorFilter.h" -#include "../render/Colors.h" #include "../render/CBitmapHandler.h" #include "../render/CDefFile.h" -#include "../render/Graphics.h" -#include "../xBRZ/xbrz.h" #include "../gui/CGuiHandler.h" #include "../render/IScreenHandler.h" @@ -145,82 +143,14 @@ void SDLImageShared::optimizeSurface() if (!surf) return; - int left = surf->w; - int top = surf->h; - int right = 0; - int bottom = 0; + SDLImageOptimizer optimizer(surf, Rect(margins, fullSize)); - // locate fully-transparent area around image - // H3 hadles this on format level, but mods or images scaled in runtime do not - if (surf->format->palette) - { - for (int y = 0; y < surf->h; ++y) - { - const uint8_t * row = static_cast(surf->pixels) + y * surf->pitch; - for (int x = 0; x < surf->w; ++x) - { - if (row[x] != 0) - { - // opaque or can be opaque (e.g. disabled shadow) - top = std::min(top, y); - left = std::min(left, x); - right = std::max(right, x); - bottom = std::max(bottom, y); - } - } - } - } - else - { - for (int y = 0; y < surf->h; ++y) - { - for (int x = 0; x < surf->w; ++x) - { - ColorRGBA color; - SDL_GetRGBA(CSDL_Ext::getPixel(surf, x, y), surf->format, &color.r, &color.g, &color.b, &color.a); + optimizer.optimizeSurface(surf); + SDL_FreeSurface(surf); - if (color.a != SDL_ALPHA_TRANSPARENT) - { - // opaque - top = std::min(top, y); - left = std::min(left, x); - right = std::max(right, x); - bottom = std::max(bottom, y); - } - } - } - } - - if (left == surf->w) - { - // empty image - simply delete it - SDL_FreeSurface(surf); - surf = nullptr; - return; - } - - if (left != 0 || top != 0 || right != surf->w - 1 || bottom != surf->h - 1) - { - // non-zero border found - Rect newDimensions(left, top, right - left + 1, bottom - top + 1); - SDL_Rect rectSDL = CSDL_Ext::toSDL(newDimensions); - auto newSurface = CSDL_Ext::newSurface(newDimensions.dimensions(), surf); - SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_NONE); - SDL_BlitSurface(surf, &rectSDL, newSurface, nullptr); - - if (SDL_HasColorKey(surf)) - { - uint32_t colorKey; - SDL_GetColorKey(surf, &colorKey); - SDL_SetColorKey(newSurface, SDL_TRUE, colorKey); - } - - SDL_FreeSurface(surf); - surf = newSurface; - - margins.x += left; - margins.y += top; - } + surf = optimizer.acquireResultSurface(); + margins = optimizer.getResultDimensions().topLeft(); + fullSize = optimizer.getResultDimensions().dimensions(); } std::shared_ptr SDLImageShared::scaleInteger(int factor, SDL_Palette * palette, EImageBlitMode mode) const @@ -234,22 +164,20 @@ std::shared_ptr SDLImageShared::scaleInteger(int factor, SDL if (palette && surf->format->palette) SDL_SetSurfacePalette(surf, palette); - SDL_Surface * scaled = nullptr; + SDLImageScaler scaler(surf, Rect(margins, fullSize)); // dump heuristics to differentiate tileable UI elements from map object / combat assets if (mode == EImageBlitMode::OPAQUE || mode == EImageBlitMode::COLORKEY || mode == EImageBlitMode::SIMPLE) - scaled = CSDL_Ext::scaleSurfaceIntegerFactor(surf, factor, EScalingAlgorithm::XBRZ_OPAQUE); + scaler.scaleSurfaceIntegerFactor(GH.screenHandler().getScalingFactor(), EScalingAlgorithm::XBRZ_OPAQUE); else - scaled = CSDL_Ext::scaleSurfaceIntegerFactor(surf, factor, EScalingAlgorithm::XBRZ_ALPHA); + scaler.scaleSurfaceIntegerFactor(GH.screenHandler().getScalingFactor(), EScalingAlgorithm::XBRZ_ALPHA); + + SDL_Surface * scaled = scaler.acquireResultSurface(); auto ret = std::make_shared(scaled); - ret->fullSize.x = fullSize.x * factor; - ret->fullSize.y = fullSize.y * factor; - - ret->margins.x = (int) round((float)margins.x * factor); - ret->margins.y = (int) round((float)margins.y * factor); - ret->optimizeSurface(); + ret->fullSize = scaler.getResultDimensions().dimensions(); + ret->margins = scaler.getResultDimensions().topLeft(); // erase our own reference SDL_FreeSurface(scaled); @@ -262,13 +190,14 @@ std::shared_ptr SDLImageShared::scaleInteger(int factor, SDL std::shared_ptr SDLImageShared::scaleTo(const Point & size, SDL_Palette * palette) const { - float scaleX = static_cast(size.x) / fullSize.x; - float scaleY = static_cast(size.y) / fullSize.y; - if (palette && surf->format->palette) SDL_SetSurfacePalette(surf, palette); - auto scaled = CSDL_Ext::scaleSurface(surf, (int)(surf->w * scaleX), (int)(surf->h * scaleY), EScalingAlgorithm::BILINEAR); + SDLImageScaler scaler(surf, Rect(margins, fullSize)); + + scaler.scaleSurface(size, EScalingAlgorithm::XBRZ_ALPHA); + + auto scaled = scaler.acquireResultSurface(); 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]); @@ -278,12 +207,8 @@ std::shared_ptr SDLImageShared::scaleTo(const Point & size, CSDL_Ext::setDefaultColorKey(scaled);//just in case auto ret = std::make_shared(scaled); - - ret->fullSize.x = (int) round((float)fullSize.x * scaleX); - ret->fullSize.y = (int) round((float)fullSize.y * scaleY); - - ret->margins.x = (int) round((float)margins.x * scaleX); - ret->margins.y = (int) round((float)margins.y * scaleY); + ret->fullSize = scaler.getResultDimensions().dimensions(); + ret->margins = scaler.getResultDimensions().topLeft(); // erase our own reference SDL_FreeSurface(scaled); diff --git a/client/renderSDL/SDLImageScaler.cpp b/client/renderSDL/SDLImageScaler.cpp new file mode 100644 index 000000000..e3b5451e5 --- /dev/null +++ b/client/renderSDL/SDLImageScaler.cpp @@ -0,0 +1,233 @@ +/* + * SDLImageScaler.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 "SDLImageScaler.h" + +#include "SDL_Extensions.h" + +#include "../CMT.h" +#include "../xBRZ/xbrz.h" + +#include +#include + +SDLImageOptimizer::SDLImageOptimizer(SDL_Surface * surf, const Rect & virtualDimensions) + : surf(surf) + , virtualDimensions(virtualDimensions) +{ +} + +void SDLImageOptimizer::optimizeSurface(SDL_Surface * formatSourceSurface) +{ + if (!surf) + return; + + int left = surf->w; + int top = surf->h; + int right = 0; + int bottom = 0; + + // locate fully-transparent area around image + // H3 hadles this on format level, but mods or images scaled in runtime do not + if (surf->format->palette) + { + for (int y = 0; y < surf->h; ++y) + { + const uint8_t * row = static_cast(surf->pixels) + y * surf->pitch; + for (int x = 0; x < surf->w; ++x) + { + if (row[x] != 0) + { + // opaque or can be opaque (e.g. disabled shadow) + top = std::min(top, y); + left = std::min(left, x); + right = std::max(right, x); + bottom = std::max(bottom, y); + } + } + } + } + else + { + for (int y = 0; y < surf->h; ++y) + { + for (int x = 0; x < surf->w; ++x) + { + ColorRGBA color; + SDL_GetRGBA(CSDL_Ext::getPixel(surf, x, y), surf->format, &color.r, &color.g, &color.b, &color.a); + + if (color.a != SDL_ALPHA_TRANSPARENT) + { + // opaque + top = std::min(top, y); + left = std::min(left, x); + right = std::max(right, x); + bottom = std::max(bottom, y); + } + } + } + } + + // empty image + if (left == surf->w) + return; + + if (left != 0 || top != 0 || right != surf->w - 1 || bottom != surf->h - 1) + { + // non-zero border found + Rect newDimensions(left, top, right - left + 1, bottom - top + 1); + SDL_Rect rectSDL = CSDL_Ext::toSDL(newDimensions); + auto newSurface = CSDL_Ext::newSurface(newDimensions.dimensions(), formatSourceSurface); + SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_NONE); + SDL_BlitSurface(surf, &rectSDL, newSurface, nullptr); + + if (SDL_HasColorKey(surf)) + { + uint32_t colorKey; + SDL_GetColorKey(surf, &colorKey); + SDL_SetColorKey(newSurface, SDL_TRUE, colorKey); + } + output = newSurface; + + virtualDimensions.x += left; + virtualDimensions.y += top; + } + else + { + output = surf; + output->refcount += 1; + } +} + +SDL_Surface * SDLImageOptimizer::acquireResultSurface() +{ + SDL_Surface * result = output; + output = nullptr; + return result; +} + +const Rect & SDLImageOptimizer::getResultDimensions() const +{ + return virtualDimensions; +} + +void SDLImageScaler::scaleSurface(Point targetDimensions, EScalingAlgorithm algorithm) +{ + if(!targetDimensions.x || !targetDimensions.y) + throw std::runtime_error("invalid scaling dimensions!"); + + Point inputSurfaceSize(intermediate->w, intermediate->h); + Point outputSurfaceSize = targetDimensions * inputSurfaceSize / virtualDimensionsInput.dimensions(); + Point outputMargins = targetDimensions * virtualDimensionsInput.topLeft() / virtualDimensionsInput.dimensions(); + + // TODO: use xBRZ if possible? E.g. when scaling to 150% do 100% -> 200% via xBRZ and then linear downscale 200% -> 150%? + // Need to investigate which is optimal for performance and for visuals + ret = CSDL_Ext::newSurface(Point(outputSurfaceSize.x, outputSurfaceSize.y), intermediate); + + virtualDimensionsOutput = Rect(outputMargins, targetDimensions); // TODO: account for input virtual size + + const uint32_t * srcPixels = static_cast(intermediate->pixels); + uint32_t * dstPixels = static_cast(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); +} + +void SDLImageScaler::scaleSurfaceIntegerFactor(int factor, EScalingAlgorithm algorithm) +{ + if(factor == 0) + throw std::runtime_error("invalid scaling factor!"); + + int newWidth = intermediate->w * factor; + int newHight = intermediate->h * factor; + + virtualDimensionsOutput = virtualDimensionsInput * factor; + + ret = CSDL_Ext::newSurface(Point(newWidth, newHight), intermediate); + + assert(intermediate->pitch == intermediate->w * 4); + assert(ret->pitch == ret->w * 4); + + const uint32_t * srcPixels = static_cast(intermediate->pixels); + uint32_t * dstPixels = static_cast(ret->pixels); + + switch (algorithm) + { + case EScalingAlgorithm::NEAREST: + xbrz::nearestNeighborScale(srcPixels, intermediate->w, intermediate->h, dstPixels, ret->w, ret->h); + break; + case EScalingAlgorithm::BILINEAR: + xbrz::bilinearScale(srcPixels, intermediate->w, intermediate->h, dstPixels, ret->w, ret->h); + break; + case EScalingAlgorithm::XBRZ_ALPHA: + case EScalingAlgorithm::XBRZ_OPAQUE: + { + auto format = algorithm == EScalingAlgorithm::XBRZ_OPAQUE ? xbrz::ColorFormat::ARGB_CLAMPED : xbrz::ColorFormat::ARGB; + + if(intermediate->h < 32) + { + // for tiny images tbb incurs too high overhead + xbrz::scale(factor, srcPixels, dstPixels, intermediate->w, intermediate->h, format, {}); + } + else + { + // xbrz recommends granulation of 16, but according to tests, for smaller images granulation of 4 is actually the best option + const int granulation = intermediate->h > 400 ? 16 : 4; + tbb::parallel_for(tbb::blocked_range(0, intermediate->h, granulation), [this, factor, srcPixels, dstPixels, format](const tbb::blocked_range & r) + { + xbrz::scale(factor, srcPixels, dstPixels, intermediate->w, intermediate->h, format, {}, r.begin(), r.end()); + }); + } + + break; + } + default: + throw std::runtime_error("invalid scaling algorithm!"); + } +} + +SDLImageScaler::SDLImageScaler(SDL_Surface * surf) + :SDLImageScaler(surf, Rect(0,0,surf->w, surf->h)) +{ +} + +SDLImageScaler::SDLImageScaler(SDL_Surface * surf, const Rect & virtualDimensions) +{ + SDLImageOptimizer optimizer(surf, virtualDimensions); + optimizer.optimizeSurface(screen); + intermediate = optimizer.acquireResultSurface(); + virtualDimensionsInput = optimizer.getResultDimensions(); + + if (intermediate == surf) + { + SDL_FreeSurface(intermediate); + intermediate = SDL_ConvertSurfaceFormat(surf, SDL_PIXELFORMAT_ARGB8888, 0); + } +} + +SDLImageScaler::~SDLImageScaler() +{ + SDL_FreeSurface(intermediate); + SDL_FreeSurface(ret); +} + +SDL_Surface * SDLImageScaler::acquireResultSurface() +{ + SDL_Surface * result = ret; + ret = nullptr; + return result; +} + +const Rect & SDLImageScaler::getResultDimensions() const +{ + return virtualDimensionsOutput; +} diff --git a/client/renderSDL/SDLImageScaler.h b/client/renderSDL/SDLImageScaler.h new file mode 100644 index 000000000..b3f94534e --- /dev/null +++ b/client/renderSDL/SDLImageScaler.h @@ -0,0 +1,63 @@ +/* + * SDLImageScaler.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" +#include "../../lib/Rect.h" + +class SDLImageOptimizer : boost::noncopyable +{ + SDL_Surface * surf = nullptr; + SDL_Surface * output = nullptr; + Rect virtualDimensions = Rect(0,0,0,0); +public: + SDLImageOptimizer(SDL_Surface * surf, const Rect & virtualDimensions); + + void optimizeSurface(SDL_Surface * formatSourceSurface); + + /// Aquires resulting surface and transfers surface ownership to the caller + /// May return nullptr if input image was empty + SDL_Surface * acquireResultSurface(); + + /// Returns adjusted virtual dimensions of resulting surface + const Rect & getResultDimensions() const; +}; + +/// Class that performs scaling of SDL surfaces +/// Object construction MUST be performed while holding UI lock +/// but task execution can be performed asynchronously if needed +class SDLImageScaler : boost::noncopyable +{ + SDL_Surface * intermediate = nullptr; + SDL_Surface * ret = nullptr; + Rect virtualDimensionsInput = Rect(0,0,0,0); + Rect virtualDimensionsOutput = Rect(0,0,0,0); + +public: + SDLImageScaler(SDL_Surface * surf); + SDLImageScaler(SDL_Surface * surf, const Rect & virtualDimensions); + ~SDLImageScaler(); + + /// Performs upscaling or downscaling to a requested dimensions + /// Aspect ratio is NOT maintained. + /// xbrz algorithm is not supported in this mode + void scaleSurface(Point dimensions, EScalingAlgorithm algorithm); + + /// Performs upscaling by specified integral factor, potentially using xbrz algorithm if requested + void scaleSurfaceIntegerFactor(int factor, EScalingAlgorithm algorithm); + + /// Aquires resulting surface and transfers surface ownership to the caller + /// May return nullptr if input image was empty + SDL_Surface * acquireResultSurface(); + + /// Returns adjusted virtual dimensions of resulting surface + const Rect & getResultDimensions() const; +}; diff --git a/client/renderSDL/SDL_Extensions.cpp b/client/renderSDL/SDL_Extensions.cpp index d0602240b..38818999f 100644 --- a/client/renderSDL/SDL_Extensions.cpp +++ b/client/renderSDL/SDL_Extensions.cpp @@ -631,89 +631,6 @@ 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, EScalingAlgorithm algorithm) -{ - if(!surf || !width || !height) - return nullptr; - - // TODO: use xBRZ if possible? E.g. when scaling to 150% do 100% -> 200% via xBRZ and then linear downscale 200% -> 150%? - // Need to investigate which is optimal for performance and for visuals - - SDL_Surface * intermediate = SDL_ConvertSurface(surf, screen->format, 0); - SDL_Surface * ret = newSurface(Point(width, height), intermediate); - - const uint32_t * srcPixels = static_cast(intermediate->pixels); - uint32_t * dstPixels = static_cast(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; -} - -SDL_Surface * CSDL_Ext::scaleSurfaceIntegerFactor(SDL_Surface * surf, int factor, EScalingAlgorithm algorithm) -{ - if(surf == nullptr || factor == 0) - return nullptr; - - int newWidth = surf->w * factor; - int newHight = surf->h * factor; - - SDL_Surface * intermediate = SDL_ConvertSurfaceFormat(surf, SDL_PIXELFORMAT_ARGB8888, 0); - SDL_Surface * ret = newSurface(Point(newWidth, newHight), intermediate); - - assert(intermediate->pitch == intermediate->w * 4); - assert(ret->pitch == ret->w * 4); - - const uint32_t * srcPixels = static_cast(intermediate->pixels); - uint32_t * dstPixels = static_cast(ret->pixels); - - switch (algorithm) - { - case EScalingAlgorithm::NEAREST: - xbrz::nearestNeighborScale(srcPixels, intermediate->w, intermediate->h, dstPixels, ret->w, ret->h); - break; - case EScalingAlgorithm::BILINEAR: - xbrz::bilinearScale(srcPixels, intermediate->w, intermediate->h, dstPixels, ret->w, ret->h); - break; - case EScalingAlgorithm::XBRZ_ALPHA: - case EScalingAlgorithm::XBRZ_OPAQUE: - { - auto format = algorithm == EScalingAlgorithm::XBRZ_OPAQUE ? xbrz::ColorFormat::ARGB_CLAMPED : xbrz::ColorFormat::ARGB; - - if(intermediate->h < 32) - { - // for tiny images tbb incurs too high overhead - xbrz::scale(factor, srcPixels, dstPixels, intermediate->w, intermediate->h, format, {}); - } - else - { - // xbrz recommends granulation of 16, but according to tests, for smaller images granulation of 4 is actually the best option - const int granulation = intermediate->h > 400 ? 16 : 4; - tbb::parallel_for(tbb::blocked_range(0, intermediate->h, granulation), [factor, srcPixels, dstPixels, intermediate, format](const tbb::blocked_range & r) - { - xbrz::scale(factor, srcPixels, dstPixels, intermediate->w, intermediate->h, format, {}, r.begin(), r.end()); - }); - } - - break; - } - default: - throw std::runtime_error("invalid scaling algorithm!"); - } - - SDL_FreeSurface(intermediate); - - return ret; -} - void CSDL_Ext::blitSurface(SDL_Surface * src, const Rect & srcRectInput, SDL_Surface * dst, const Point & dstPoint) { SDL_Rect srcRect = CSDL_Ext::toSDL(srcRectInput); diff --git a/client/renderSDL/SDL_Extensions.h b/client/renderSDL/SDL_Extensions.h index 8df7fb50c..6d01d2520 100644 --- a/client/renderSDL/SDL_Extensions.h +++ b/client/renderSDL/SDL_Extensions.h @@ -18,7 +18,6 @@ struct SDL_Renderer; struct SDL_Texture; struct SDL_Surface; struct SDL_Color; -enum class EScalingAlgorithm : int8_t; VCMI_LIB_NAMESPACE_BEGIN @@ -91,10 +90,6 @@ using TColorPutterAlpha = void (*)(uint8_t *&, const uint8_t &, const uint8_t &, template 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, EScalingAlgorithm scaler); - SDL_Surface * scaleSurfaceIntegerFactor(SDL_Surface * surf, int factor, EScalingAlgorithm scaler); - template void convertToGrayscaleBpp(SDL_Surface * surf, const Rect & rect); void convertToGrayscale(SDL_Surface * surf, const Rect & rect); diff --git a/client/renderSDL/ScalableImage.cpp b/client/renderSDL/ScalableImage.cpp index 39fe922b8..2c76c254d 100644 --- a/client/renderSDL/ScalableImage.cpp +++ b/client/renderSDL/ScalableImage.cpp @@ -260,7 +260,7 @@ void ScalableImageShared::draw(SDL_Surface * where, const Point & dest, const Re if (scaled.at(scalingFactor).shadow.at(0)) flipAndDraw(scaled.at(scalingFactor).shadow, Colors::WHITE_TRUE, parameters.alphaValue); - if (parameters.player != PlayerColor::CANNOT_DETERMINE) + if (parameters.player.isValidPlayer()) { scaled.at(scalingFactor).playerColored[parameters.player.getNum()]->draw(where, parameters.palette, dest, src, Colors::WHITE_TRUE, parameters.alphaValue, locator.layer); } @@ -351,7 +351,7 @@ void ScalableImageInstance::playerColored(const PlayerColor & player) { parameters.player = player; - if (!parameters.palette) + if (parameters.palette) parameters.playerColored(player); image->preparePlayerColoredImage(player); diff --git a/client/renderSDL/ScalableImage.h b/client/renderSDL/ScalableImage.h index 5bb68f855..31ce0f303 100644 --- a/client/renderSDL/ScalableImage.h +++ b/client/renderSDL/ScalableImage.h @@ -15,9 +15,6 @@ #include "../../lib/Color.h" -#include -#include - struct SDL_Palette; class ScalableImageInstance; @@ -47,24 +44,6 @@ struct ScalableImageParameters : boost::noncopyable void adjustPalette(const SDL_Palette * originalPalette, EImageBlitMode blitMode, const ColorFilter & shifter, uint32_t colorsToSkipMask); }; -class ImageScaler -{ - ImageScaler(); - - tbb::task_arena arena; -public: - static ImageScaler & getInstance() - { - static ImageScaler scaler; - return scaler; - } - - void enqueueTask(const std::function & task) - { - arena.enqueue(task); - } -}; - class ScalableImageShared final : public std::enable_shared_from_this, boost::noncopyable { static constexpr int scalingSize = 5; // 0-4 range. TODO: switch to either 2-4 or 1-4 @@ -97,12 +76,6 @@ class ScalableImageShared final : public std::enable_shared_from_this> upscalingQueue; - - /// Number of images that are currently being upscaled - int scheduledUpscalingEvents = 0; - std::shared_ptr loadOrGenerateImage(EImageBlitMode mode, int8_t scalingFactor, PlayerColor color) const; void loadScaledImages(int8_t scalingFactor, PlayerColor color); diff --git a/lib/Point.h b/lib/Point.h index fd3bde33c..afc87c702 100644 --- a/lib/Point.h +++ b/lib/Point.h @@ -54,6 +54,11 @@ public: return Point(x*mul, y*mul); } + constexpr Point operator/(const Point &b) const + { + return Point(x/b.x,y/b.y); + } + constexpr Point operator*(const Point &b) const { return Point(x*b.x,y*b.y); diff --git a/lib/pathfinder/TurnInfo.cpp b/lib/pathfinder/TurnInfo.cpp index d8911332a..515f9fa14 100644 --- a/lib/pathfinder/TurnInfo.cpp +++ b/lib/pathfinder/TurnInfo.cpp @@ -120,7 +120,7 @@ TurnInfo::TurnInfo(TurnInfoCache * sharedCache, const CGHeroInstance * target, i { static const CSelector selector = Selector::type()(BonusType::ROUGH_TERRAIN_DISCOUNT); const auto & bonuses = sharedCache->roughTerrainDiscount.getBonusList(target, selector); - roughTerrainDiscountValue = bonuses->getFirst(daySelector) != nullptr; + roughTerrainDiscountValue = bonuses->valOfBonuses(daySelector); } {