diff --git a/client/render/IImage.h b/client/render/IImage.h index a6e4e8b7b..8f10dc5fd 100644 --- a/client/render/IImage.h +++ b/client/render/IImage.h @@ -115,7 +115,7 @@ public: virtual std::shared_ptr horizontalFlip() const = 0; virtual std::shared_ptr verticalFlip() const = 0; - virtual std::shared_ptr scaleInteger(int factor, SDL_Palette * palette) const = 0; + virtual std::shared_ptr scaleInteger(int factor, SDL_Palette * palette, EImageBlitMode blitMode) const = 0; virtual std::shared_ptr scaleTo(const Point & size, SDL_Palette * palette) const = 0; diff --git a/client/renderSDL/CBitmapFont.cpp b/client/renderSDL/CBitmapFont.cpp index a932e6368..4287221e7 100644 --- a/client/renderSDL/CBitmapFont.cpp +++ b/client/renderSDL/CBitmapFont.cpp @@ -201,7 +201,7 @@ CBitmapFont::CBitmapFont(const std::string & filename): static const std::map filterNameToEnum = { { "nearest", EScalingAlgorithm::NEAREST}, { "bilinear", EScalingAlgorithm::BILINEAR}, - { "xbrz", EScalingAlgorithm::XBRZ} + { "xbrz", EScalingAlgorithm::XBRZ_ALPHA} }; auto filterName = settings["video"]["fontUpscalingFilter"].String(); diff --git a/client/renderSDL/SDLImage.cpp b/client/renderSDL/SDLImage.cpp index 84d37e7ee..df10c01d8 100644 --- a/client/renderSDL/SDLImage.cpp +++ b/client/renderSDL/SDLImage.cpp @@ -278,7 +278,7 @@ void SDLImageShared::optimizeSurface() } } -std::shared_ptr SDLImageShared::scaleInteger(int factor, SDL_Palette * palette) const +std::shared_ptr SDLImageShared::scaleInteger(int factor, SDL_Palette * palette, EImageBlitMode mode) const { if (factor <= 0) throw std::runtime_error("Unable to scale by integer value of " + std::to_string(factor)); @@ -293,7 +293,13 @@ std::shared_ptr SDLImageShared::scaleInteger(int factor, SDL if(preScaleFactor == factor) return shared_from_this(); else if(preScaleFactor == 1) - scaled = CSDL_Ext::scaleSurfaceIntegerFactor(surf, factor, EScalingAlgorithm::XBRZ); + { + // 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); + else + scaled = CSDL_Ext::scaleSurfaceIntegerFactor(surf, factor, EScalingAlgorithm::XBRZ_ALPHA); + } else scaled = CSDL_Ext::scaleSurface(surf, (surf->w / preScaleFactor) * factor, (surf->h / preScaleFactor) * factor); @@ -589,12 +595,12 @@ void SDLImageRGB::scaleTo(const Point & size) void SDLImageIndexed::scaleInteger(int factor) { - image = image->scaleInteger(factor, currentPalette); + image = image->scaleInteger(factor, currentPalette, blitMode); } void SDLImageRGB::scaleInteger(int factor) { - image = image->scaleInteger(factor, nullptr); + image = image->scaleInteger(factor, nullptr, blitMode); } void SDLImageRGB::exportBitmap(const boost::filesystem::path & path) const diff --git a/client/renderSDL/SDLImage.h b/client/renderSDL/SDLImage.h index 833ccec42..c1f6fcf77 100644 --- a/client/renderSDL/SDLImage.h +++ b/client/renderSDL/SDLImage.h @@ -60,7 +60,7 @@ public: std::shared_ptr createImageReference(EImageBlitMode mode) const override; std::shared_ptr horizontalFlip() const override; std::shared_ptr verticalFlip() const override; - std::shared_ptr scaleInteger(int factor, SDL_Palette * palette) const override; + std::shared_ptr scaleInteger(int factor, SDL_Palette * palette, EImageBlitMode blitMode) const override; std::shared_ptr scaleTo(const Point & size, SDL_Palette * palette) const override; friend class SDLImageLoader; diff --git a/client/renderSDL/SDL_Extensions.cpp b/client/renderSDL/SDL_Extensions.cpp index 237f6800d..63a9089de 100644 --- a/client/renderSDL/SDL_Extensions.cpp +++ b/client/renderSDL/SDL_Extensions.cpp @@ -683,12 +683,17 @@ SDL_Surface * CSDL_Ext::scaleSurfaceIntegerFactor(SDL_Surface * surf, int factor case EScalingAlgorithm::BILINEAR: xbrz::bilinearScale(srcPixels, intermediate->w, intermediate->h, dstPixels, ret->w, ret->h); break; - case EScalingAlgorithm::XBRZ: - tbb::parallel_for(tbb::blocked_range(0, intermediate->h, granulation), [factor, srcPixels, dstPixels, intermediate](const tbb::blocked_range & r) + case EScalingAlgorithm::XBRZ_ALPHA: + case EScalingAlgorithm::XBRZ_OPAQUE: + { + auto format = algorithm == EScalingAlgorithm::XBRZ_OPAQUE ? xbrz::ColorFormat::ARGB_CLAMPED : xbrz::ColorFormat::ARGB; + 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, xbrz::ColorFormat::ARGB, {}, r.begin(), r.end()); + + xbrz::scale(factor, srcPixels, dstPixels, intermediate->w, intermediate->h, format, {}, r.begin(), r.end()); }); break; + } default: throw std::runtime_error("invalid scaling algorithm!"); } diff --git a/client/renderSDL/SDL_Extensions.h b/client/renderSDL/SDL_Extensions.h index 06fb17682..acdb0dc5a 100644 --- a/client/renderSDL/SDL_Extensions.h +++ b/client/renderSDL/SDL_Extensions.h @@ -31,7 +31,8 @@ enum class EScalingAlgorithm : int8_t { NEAREST, BILINEAR, - XBRZ + XBRZ_OPAQUE, // xbrz, image edges are considered to have same color as pixel inside image + XBRZ_ALPHA // xbrz, image edges are considered to be transparent }; namespace CSDL_Ext diff --git a/client/xBRZ/xbrz.cpp b/client/xBRZ/xbrz.cpp index 2fab57f3c..1eda5a46b 100644 --- a/client/xBRZ/xbrz.cpp +++ b/client/xBRZ/xbrz.cpp @@ -1195,6 +1195,22 @@ void xbrz::scale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth } break; + case ColorFormat::ARGB_CLAMPED: + switch (factor) + { + case 2: + return scaleImage, ColorDistanceARGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 3: + return scaleImage, ColorDistanceARGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 4: + return scaleImage, ColorDistanceARGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 5: + return scaleImage, ColorDistanceARGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 6: + return scaleImage, ColorDistanceARGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + } + break; + case ColorFormat::ARGB: switch (factor) { @@ -1238,6 +1254,7 @@ bool xbrz::equalColorTest(uint32_t col1, uint32_t col2, ColorFormat colFmt, doub case ColorFormat::RGB: return ColorDistanceRGB::dist(col1, col2, luminanceWeight) < equalColorTolerance; case ColorFormat::ARGB: + case ColorFormat::ARGB_CLAMPED: return ColorDistanceARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance; case ColorFormat::ARGB_UNBUFFERED: return ColorDistanceUnbufferedARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance; diff --git a/client/xBRZ/xbrz.h b/client/xBRZ/xbrz.h index 492fb43ad..4e646dba6 100644 --- a/client/xBRZ/xbrz.h +++ b/client/xBRZ/xbrz.h @@ -44,6 +44,7 @@ enum class ColorFormat //from high bits -> low bits, 8 bit per channel { RGB, //8 bit for each red, green, blue, upper 8 bits unused ARGB, //including alpha channel, BGRA byte order on little-endian machines + ARGB_CLAMPED, // like ARGB, but edges are treated as opaque, with same color as edge ARGB_UNBUFFERED, //like ARGB, but without the one-time buffer creation overhead (ca. 100 - 300 ms) at the expense of a slightly slower scaling time };