From 4a600a9d4ca2128ba828a3cf0ae254ac5ec6222b Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 15 Jan 2025 17:05:45 +0000 Subject: [PATCH] Reworked image container classes for easier support of new features --- client/CMakeLists.txt | 4 +- client/render/AssetGenerator.cpp | 44 +-- client/render/CAnimation.cpp | 4 +- client/render/Canvas.cpp | 8 +- client/render/Canvas.h | 2 +- client/render/IImage.h | 12 +- client/render/IRenderHandler.h | 6 +- client/render/ImageLocator.cpp | 95 +----- client/render/ImageLocator.h | 33 +- client/renderSDL/CursorHardware.cpp | 2 +- client/renderSDL/CursorSoftware.cpp | 2 +- client/renderSDL/ImageScaled.cpp | 172 ---------- client/renderSDL/ImageScaled.h | 66 ---- client/renderSDL/RenderHandler.cpp | 310 +++++++----------- client/renderSDL/RenderHandler.h | 26 +- client/renderSDL/SDLImage.cpp | 336 ++----------------- client/renderSDL/SDLImage.h | 68 +--- client/renderSDL/ScalableImage.cpp | 489 ++++++++++++++++++++++++++++ client/renderSDL/ScalableImage.h | 124 +++++++ client/widgets/Images.cpp | 8 +- client/widgets/Images.h | 2 + client/windows/CCastleInterface.cpp | 3 +- client/windows/CMapOverview.cpp | 5 +- client/windows/CWindowObject.cpp | 14 +- 24 files changed, 855 insertions(+), 980 deletions(-) delete mode 100644 client/renderSDL/ImageScaled.cpp delete mode 100644 client/renderSDL/ImageScaled.h create mode 100644 client/renderSDL/ScalableImage.cpp create mode 100644 client/renderSDL/ScalableImage.h diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index e44f6ec5e..470236b82 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -99,7 +99,7 @@ set(vcmiclientcommon_SRCS renderSDL/CursorHardware.cpp renderSDL/CursorSoftware.cpp renderSDL/FontChain.cpp - renderSDL/ImageScaled.cpp + renderSDL/ScalableImage.cpp renderSDL/RenderHandler.cpp renderSDL/SDLImage.cpp renderSDL/SDLImageLoader.cpp @@ -307,7 +307,7 @@ set(vcmiclientcommon_HEADERS renderSDL/CursorHardware.h renderSDL/CursorSoftware.h renderSDL/FontChain.h - renderSDL/ImageScaled.h + renderSDL/ScalableImage.h renderSDL/RenderHandler.h renderSDL/SDLImage.h renderSDL/SDLImageLoader.h diff --git a/client/render/AssetGenerator.cpp b/client/render/AssetGenerator.cpp index 56945f922..8974016c7 100644 --- a/client/render/AssetGenerator.cpp +++ b/client/render/AssetGenerator.cpp @@ -58,10 +58,9 @@ void AssetGenerator::createAdventureOptionsCleanBackground() return; ResourcePath savePath(filename, EResType::IMAGE); - auto locator = ImageLocator(ImagePath::builtin("ADVOPTBK")); - locator.scalingFactor = 1; + auto locator = ImageLocator(ImagePath::builtin("ADVOPTBK"), EImageBlitMode::OPAQUE); - std::shared_ptr img = GH.renderHandler().loadImage(locator, EImageBlitMode::OPAQUE); + std::shared_ptr img = GH.renderHandler().loadImage(locator); Canvas canvas = Canvas(Point(575, 585), CanvasScalingPolicy::IGNORE); canvas.draw(img, Point(0, 0), Rect(0, 0, 575, 585)); @@ -88,10 +87,9 @@ void AssetGenerator::createBigSpellBook() return; ResourcePath savePath(filename, EResType::IMAGE); - auto locator = ImageLocator(ImagePath::builtin("SpelBack")); - locator.scalingFactor = 1; + auto locator = ImageLocator(ImagePath::builtin("SpelBack"), EImageBlitMode::OPAQUE); - std::shared_ptr img = GH.renderHandler().loadImage(locator, EImageBlitMode::OPAQUE); + std::shared_ptr img = GH.renderHandler().loadImage(locator); Canvas canvas = Canvas(Point(800, 600), CanvasScalingPolicy::IGNORE); // edges canvas.draw(img, Point(0, 0), Rect(15, 38, 90, 45)); @@ -152,10 +150,9 @@ void AssetGenerator::createPlayerColoredBackground(const PlayerColor & player) ResourcePath savePath(filename, EResType::IMAGE); - auto locator = ImageLocator(ImagePath::builtin("DiBoxBck")); - locator.scalingFactor = 1; + auto locator = ImageLocator(ImagePath::builtin("DiBoxBck"), EImageBlitMode::OPAQUE); - std::shared_ptr texture = GH.renderHandler().loadImage(locator, EImageBlitMode::OPAQUE); + std::shared_ptr texture = GH.renderHandler().loadImage(locator); // transform to make color of brown DIBOX.PCX texture match color of specified player auto filterSettings = VLC->settingsHandler->getFullConfig()["interface"]["playerColoredBackground"]; @@ -199,10 +196,10 @@ void AssetGenerator::createCombatUnitNumberWindow() !CResourceHandler::get("local")->createResource(savePathNegative.getOriginalName() + ".png")) return; - auto locator = ImageLocator(ImagePath::builtin("CMNUMWIN")); - locator.scalingFactor = 1; + auto locator = ImageLocator(ImagePath::builtin("CMNUMWIN"), EImageBlitMode::OPAQUE); + locator.layer = EImageBlitMode::OPAQUE; - std::shared_ptr texture = GH.renderHandler().loadImage(locator, EImageBlitMode::OPAQUE); + std::shared_ptr texture = GH.renderHandler().loadImage(locator); static const auto shifterNormal = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 0.6f, 0.2f, 1.0f ); static const auto shifterPositive = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 0.2f, 1.0f, 0.2f ); @@ -233,10 +230,9 @@ void AssetGenerator::createCampaignBackground() return; ResourcePath savePath(filename, EResType::IMAGE); - auto locator = ImageLocator(ImagePath::builtin("CAMPBACK")); - locator.scalingFactor = 1; + auto locator = ImageLocator(ImagePath::builtin("CAMPBACK"), EImageBlitMode::OPAQUE); - std::shared_ptr img = GH.renderHandler().loadImage(locator, EImageBlitMode::OPAQUE); + std::shared_ptr img = GH.renderHandler().loadImage(locator); Canvas canvas = Canvas(Point(800, 600), CanvasScalingPolicy::IGNORE); canvas.draw(img, Point(0, 0), Rect(0, 0, 800, 600)); @@ -263,9 +259,8 @@ void AssetGenerator::createCampaignBackground() canvas.draw(img, Point(404, 414), Rect(313, 74, 197, 114)); // skull - auto locatorSkull = ImageLocator(ImagePath::builtin("CAMPNOSC")); - locatorSkull.scalingFactor = 1; - std::shared_ptr imgSkull = GH.renderHandler().loadImage(locatorSkull, EImageBlitMode::OPAQUE); + auto locatorSkull = ImageLocator(ImagePath::builtin("CAMPNOSC"), EImageBlitMode::OPAQUE); + std::shared_ptr imgSkull = GH.renderHandler().loadImage(locatorSkull); canvas.draw(imgSkull, Point(562, 509), Rect(178, 108, 43, 19)); std::shared_ptr image = GH.renderHandler().createImage(canvas.getInternalSurface()); @@ -290,10 +285,9 @@ void AssetGenerator::createChroniclesCampaignImages() continue; ResourcePath savePath(filename, EResType::IMAGE); - auto locator = ImageLocator(imgPathBg); - locator.scalingFactor = 1; + auto locator = ImageLocator(imgPathBg, EImageBlitMode::OPAQUE); - std::shared_ptr img = GH.renderHandler().loadImage(locator, EImageBlitMode::OPAQUE); + std::shared_ptr img = GH.renderHandler().loadImage(locator); Canvas canvas = Canvas(Point(200, 116), CanvasScalingPolicy::IGNORE); switch (i) @@ -323,9 +317,8 @@ void AssetGenerator::createChroniclesCampaignImages() canvas.draw(img, Point(0, 0), Rect(268, 210, 200, 116)); //skull - auto locatorSkull = ImageLocator(ImagePath::builtin("CampSP1")); - locatorSkull.scalingFactor = 1; - std::shared_ptr imgSkull = GH.renderHandler().loadImage(locatorSkull, EImageBlitMode::OPAQUE); + auto locatorSkull = ImageLocator(ImagePath::builtin("CampSP1"), EImageBlitMode::OPAQUE); + std::shared_ptr imgSkull = GH.renderHandler().loadImage(locatorSkull); canvas.draw(imgSkull, Point(162, 94), Rect(162, 94, 41, 22)); canvas.draw(img, Point(162, 94), Rect(424, 304, 14, 4)); canvas.draw(img, Point(162, 98), Rect(424, 308, 10, 4)); @@ -403,8 +396,7 @@ void AssetGenerator::createPaletteShiftedSprites() return; auto imgLoc = anim->getImageLocator(j, 0); - imgLoc.scalingFactor = 1; - auto img = GH.renderHandler().loadImage(imgLoc, EImageBlitMode::COLORKEY); + auto img = GH.renderHandler().loadImage(imgLoc); for(int k = 0; k < paletteAnimations[i].size(); k++) { auto element = paletteAnimations[i][k]; diff --git a/client/render/CAnimation.cpp b/client/render/CAnimation.cpp index 63439bf1c..d045e73a8 100644 --- a/client/render/CAnimation.cpp +++ b/client/render/CAnimation.cpp @@ -30,7 +30,7 @@ bool CAnimation::loadFrame(size_t frame, size_t group, bool verbose) if(auto image = getImageImpl(frame, group, false)) return true; - std::shared_ptr image = GH.renderHandler().loadImage(getImageLocator(frame, group), mode); + std::shared_ptr image = GH.renderHandler().loadImage(getImageLocator(frame, group)); if(image) { @@ -224,5 +224,5 @@ ImageLocator CAnimation::getImageLocator(size_t frame, size_t group) const throw std::runtime_error("Frame " + std::to_string(frame) + " of group " + std::to_string(group) + " is missing from animation " + name.getOriginalName() ); } - return ImageLocator(name, frame, group); + return ImageLocator(name, frame, group, mode); } diff --git a/client/render/Canvas.cpp b/client/render/Canvas.cpp index a89e1a6f5..943716cbd 100644 --- a/client/render/Canvas.cpp +++ b/client/render/Canvas.cpp @@ -106,7 +106,7 @@ void Canvas::draw(const std::shared_ptr& image, const Point & pos) { assert(image); if (image) - image->draw(surface, transformPos(pos)); + image->draw(surface, transformPos(pos), nullptr, getScalingFactor()); } void Canvas::draw(const std::shared_ptr& image, const Point & pos, const Rect & sourceRect) @@ -114,7 +114,7 @@ void Canvas::draw(const std::shared_ptr& image, const Point & pos, const Rect realSourceRect = sourceRect * getScalingFactor(); assert(image); if (image) - image->draw(surface, transformPos(pos), &realSourceRect); + image->draw(surface, transformPos(pos), &realSourceRect, getScalingFactor()); } void Canvas::draw(const Canvas & image, const Point & pos) @@ -218,11 +218,11 @@ void Canvas::fillTexture(const std::shared_ptr& image) for (int y=0; y < surface->h; y+= imageArea.h) { for (int x=0; x < surface->w; x+= imageArea.w) - image->draw(surface, Point(renderArea.x + x * getScalingFactor(), renderArea.y + y * getScalingFactor())); + image->draw(surface, Point(renderArea.x + x * getScalingFactor(), renderArea.y + y * getScalingFactor()), nullptr, getScalingFactor()); } } -SDL_Surface * Canvas::getInternalSurface() +SDL_Surface * Canvas::getInternalSurface() const { return surface; } diff --git a/client/render/Canvas.h b/client/render/Canvas.h index a421ab232..5a044ed9b 100644 --- a/client/render/Canvas.h +++ b/client/render/Canvas.h @@ -115,7 +115,7 @@ public: int getScalingFactor() const; /// Compatibility method. AVOID USAGE. To be removed once SDL abstraction layer is finished. - SDL_Surface * getInternalSurface(); + SDL_Surface * getInternalSurface() const; /// get the render area Rect getRenderArea() const; diff --git a/client/render/IImage.h b/client/render/IImage.h index 51f0e24e2..bb8b3cc02 100644 --- a/client/render/IImage.h +++ b/client/render/IImage.h @@ -68,10 +68,9 @@ class IImage { public: //draws image on surface "where" at position - virtual void draw(SDL_Surface * where, const Point & pos, const Rect * src = nullptr) const = 0; + virtual void draw(SDL_Surface * where, const Point & pos, const Rect * src, int scalingFactor) const = 0; virtual void scaleTo(const Point & size) = 0; - virtual void scaleInteger(int factor) = 0; virtual void exportBitmap(const boost::filesystem::path & path) const = 0; @@ -92,13 +91,10 @@ public: virtual void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) = 0; virtual void setAlpha(uint8_t value) = 0; - virtual void setBlitMode(EImageBlitMode mode) = 0; //only indexed bitmaps with 7 special colors virtual void setOverlayColor(const ColorRGBA & color) = 0; - virtual std::shared_ptr getSharedImage() const = 0; - virtual ~IImage() = default; }; @@ -114,13 +110,13 @@ public: virtual Rect contentRect() 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; - [[nodiscard]] virtual std::shared_ptr createImageReference(EImageBlitMode mode) const = 0; + virtual ~ISharedImage() = default; + + virtual const SDL_Palette * getPalette() const = 0; [[nodiscard]] virtual std::shared_ptr horizontalFlip() const = 0; [[nodiscard]] virtual std::shared_ptr verticalFlip() const = 0; [[nodiscard]] virtual std::shared_ptr scaleInteger(int factor, SDL_Palette * palette, EImageBlitMode blitMode) const = 0; [[nodiscard]] virtual std::shared_ptr scaleTo(const Point & size, SDL_Palette * palette) const = 0; - - virtual ~ISharedImage() = default; }; diff --git a/client/render/IRenderHandler.h b/client/render/IRenderHandler.h index b3b0deb19..25cdc55d0 100644 --- a/client/render/IRenderHandler.h +++ b/client/render/IRenderHandler.h @@ -20,6 +20,7 @@ struct SDL_Surface; class IFont; class IImage; class CAnimation; +class SDLImageShared; enum class EImageBlitMode : uint8_t; enum EFonts : int8_t; @@ -32,10 +33,13 @@ public: virtual void onLibraryLoadingFinished(const Services * services) = 0; /// Loads image using given path - virtual std::shared_ptr loadImage(const ImageLocator & locator, EImageBlitMode mode) = 0; + virtual std::shared_ptr loadImage(const ImageLocator & locator) = 0; virtual std::shared_ptr loadImage(const ImagePath & path, EImageBlitMode mode) = 0; virtual std::shared_ptr loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode) = 0; + /// Loads single image without scaling support + virtual std::shared_ptr loadSingleImage(const ImageLocator & locator) = 0; + /// temporary compatibility method. Creates IImage from existing SDL_Surface /// Surface will be shared, caller must still free it with SDL_FreeSurface virtual std::shared_ptr createImage(SDL_Surface * source) = 0; diff --git a/client/render/ImageLocator.cpp b/client/render/ImageLocator.cpp index 515e767a0..ddc10e49f 100644 --- a/client/render/ImageLocator.cpp +++ b/client/render/ImageLocator.cpp @@ -15,11 +15,10 @@ #include "../../lib/json/JsonNode.h" -ImageLocator::ImageLocator(const JsonNode & config) +SharedImageLocator::SharedImageLocator(const JsonNode & config, EImageBlitMode mode) : defFrame(config["defFrame"].Integer()) , defGroup(config["defGroup"].Integer()) - , verticalFlip(config["verticalFlip"].Bool()) - , horizontalFlip(config["horizontalFlip"].Bool()) + , layer(mode) { if(!config["file"].isNull()) image = ImagePath::fromJson(config["file"]); @@ -28,19 +27,28 @@ ImageLocator::ImageLocator(const JsonNode & config) defFile = AnimationPath::fromJson(config["defFile"]); } -ImageLocator::ImageLocator(const ImagePath & path) +SharedImageLocator::SharedImageLocator(const ImagePath & path, EImageBlitMode mode) : image(path) + , layer(mode) { } -ImageLocator::ImageLocator(const AnimationPath & path, int frame, int group) +SharedImageLocator::SharedImageLocator(const AnimationPath & path, int frame, int group, EImageBlitMode mode) : defFile(path) , defFrame(frame) , defGroup(group) + , layer(mode) { } -bool ImageLocator::operator<(const ImageLocator & other) const +ImageLocator::ImageLocator(const JsonNode & config, EImageBlitMode mode) + : SharedImageLocator(config, mode) + , verticalFlip(config["verticalFlip"].Bool()) + , horizontalFlip(config["horizontalFlip"].Bool()) +{ +} + +bool SharedImageLocator::operator < (const SharedImageLocator & other) const { if(image != other.image) return image < other.image; @@ -50,14 +58,6 @@ bool ImageLocator::operator<(const ImageLocator & other) const return defGroup < other.defGroup; if(defFrame != other.defFrame) return defFrame < other.defFrame; - if(verticalFlip != other.verticalFlip) - return verticalFlip < other.verticalFlip; - if(horizontalFlip != other.horizontalFlip) - return horizontalFlip < other.horizontalFlip; - if(scalingFactor != other.scalingFactor) - return scalingFactor < other.scalingFactor; - if(playerColored != other.playerColored) - return playerColored < other.playerColored; if(layer != other.layer) return layer < other.layer; @@ -68,70 +68,3 @@ bool ImageLocator::empty() const { return !image.has_value() && !defFile.has_value(); } - -ImageLocator ImageLocator::copyFile() const -{ - ImageLocator result; - result.scalingFactor = 1; - result.preScaledFactor = preScaledFactor; - result.image = image; - result.defFile = defFile; - result.defFrame = defFrame; - result.defGroup = defGroup; - return result; -} - -ImageLocator ImageLocator::copyFileTransform() const -{ - ImageLocator result = copyFile(); - result.horizontalFlip = horizontalFlip; - result.verticalFlip = verticalFlip; - return result; -} - -ImageLocator ImageLocator::copyFileTransformScale() const -{ - return *this; // full copy -} - -std::string ImageLocator::toString() const -{ - std::string result; - if (empty()) - return "invalid"; - - if (image) - { - result += image->getOriginalName(); - assert(!result.empty()); - } - - if (defFile) - { - result += defFile->getOriginalName(); - assert(!result.empty()); - result += "-" + std::to_string(defGroup); - result += "-" + std::to_string(defFrame); - } - - if (verticalFlip) - result += "-vflip"; - - if (horizontalFlip) - result += "-hflip"; - - if (scalingFactor > 1) - result += "-scale" + std::to_string(scalingFactor); - - if (playerColored.isValidPlayer()) - result += "-player" + playerColored.toString(); - - if (layer == EImageBlitMode::ONLY_OVERLAY) - result += "-overlay"; - - if (layer == EImageBlitMode::ONLY_SHADOW) - result += "-shadow"; - - - return result; -} diff --git a/client/render/ImageLocator.h b/client/render/ImageLocator.h index 1868caaf2..59754b2b6 100644 --- a/client/render/ImageLocator.h +++ b/client/render/ImageLocator.h @@ -14,35 +14,32 @@ #include "../../lib/filesystem/ResourcePath.h" #include "../../lib/constants/EntityIdentifiers.h" -struct ImageLocator +struct SharedImageLocator { std::optional image; std::optional defFile; int defFrame = -1; int defGroup = -1; + EImageBlitMode layer = EImageBlitMode::OPAQUE; - PlayerColor playerColored = PlayerColor::CANNOT_DETERMINE; // FIXME: treat as identical to blue to avoid double-loading? + SharedImageLocator() = default; + SharedImageLocator(const AnimationPath & path, int frame, int group, EImageBlitMode layer); + SharedImageLocator(const JsonNode & config, EImageBlitMode layer); + SharedImageLocator(const ImagePath & path, EImageBlitMode layer); + + bool operator < (const SharedImageLocator & other) const; +}; + +struct ImageLocator : SharedImageLocator +{ + PlayerColor playerColored = PlayerColor::CANNOT_DETERMINE; bool verticalFlip = false; bool horizontalFlip = false; int8_t scalingFactor = 0; // 0 = auto / use default scaling - int8_t preScaledFactor = 1; - EImageBlitMode layer = EImageBlitMode::OPAQUE; - ImageLocator() = default; - ImageLocator(const AnimationPath & path, int frame, int group); - explicit ImageLocator(const JsonNode & config); - explicit ImageLocator(const ImagePath & path); + using SharedImageLocator::SharedImageLocator; + ImageLocator(const JsonNode & config, EImageBlitMode layer); - bool operator < (const ImageLocator & other) const; bool empty() const; - - ImageLocator copyFile() const; - ImageLocator copyFileTransform() const; - ImageLocator copyFileTransformScale() const; - - // generates string representation of this image locator - // guaranteed to be a valid file path with no extension - // but may contain '/' if source file is in directory - std::string toString() const; }; diff --git a/client/renderSDL/CursorHardware.cpp b/client/renderSDL/CursorHardware.cpp index 7c9daea4d..27f393dde 100644 --- a/client/renderSDL/CursorHardware.cpp +++ b/client/renderSDL/CursorHardware.cpp @@ -59,7 +59,7 @@ 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)); + image->draw(cursorSurface, Point(0,0), nullptr, GH.screenHandler().getScalingFactor()); auto cursorSurfaceScaled = CSDL_Ext::scaleSurface(cursorSurface, cursorDimensionsScaled.x, cursorDimensionsScaled.y ); auto oldCursor = cursor; diff --git a/client/renderSDL/CursorSoftware.cpp b/client/renderSDL/CursorSoftware.cpp index e5d5a9c09..1e84ecd06 100644 --- a/client/renderSDL/CursorSoftware.cpp +++ b/client/renderSDL/CursorSoftware.cpp @@ -65,7 +65,7 @@ void CursorSoftware::updateTexture() CSDL_Ext::fillSurface(cursorSurface, CSDL_Ext::toSDL(Colors::TRANSPARENCY)); - cursorImage->draw(cursorSurface, Point(0,0)); + cursorImage->draw(cursorSurface, Point(0,0), nullptr, GH.screenHandler().getScalingFactor()); SDL_UpdateTexture(cursorTexture, nullptr, cursorSurface->pixels, cursorSurface->pitch); needUpdate = false; } diff --git a/client/renderSDL/ImageScaled.cpp b/client/renderSDL/ImageScaled.cpp deleted file mode 100644 index 55ed44cd1..000000000 --- a/client/renderSDL/ImageScaled.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/* - * 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 "../gui/CGuiHandler.h" -#include "../render/IScreenHandler.h" -#include "../render/Colors.h" - -#include "../../lib/constants/EntityIdentifiers.h" - -#include - -ImageScaled::ImageScaled(const ImageLocator & inputLocator, const std::shared_ptr & source, EImageBlitMode mode) - : source(source) - , locator(inputLocator) - , colorMultiplier(Colors::WHITE_TRUE) - , alphaValue(SDL_ALPHA_OPAQUE) - , blitMode(mode) -{ - prepareImages(); -} - -std::shared_ptr ImageScaled::getSharedImage() const -{ - return body; -} - -void ImageScaled::scaleInteger(int factor) -{ - assert(0); -} - -void ImageScaled::scaleTo(const Point & size) -{ - if (source) - source = source->scaleTo(size, nullptr); - - if (body) - body = body->scaleTo(size * GH.screenHandler().getScalingFactor(), nullptr); -} - -void ImageScaled::exportBitmap(const boost::filesystem::path &path) const -{ - source->exportBitmap(path, nullptr); -} - -bool ImageScaled::isTransparent(const Point &coords) const -{ - return source->isTransparent(coords); -} - -Rect ImageScaled::contentRect() const -{ - return source->contentRect(); -} - -Point ImageScaled::dimensions() const -{ - return source->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 -{ - if (shadow) - shadow->draw(where, nullptr, pos, src, Colors::WHITE_TRUE, alphaValue, blitMode); - if (body) - body->draw(where, nullptr, pos, src, Colors::WHITE_TRUE, alphaValue, blitMode); - if (overlay) - overlay->draw(where, nullptr, pos, src, colorMultiplier, colorMultiplier.a * alphaValue / 255, blitMode); -} - -void ImageScaled::setOverlayColor(const ColorRGBA & color) -{ - colorMultiplier = color; -} - -void ImageScaled::playerColored(PlayerColor player) -{ - playerColor = player; - prepareImages(); -} - -void ImageScaled::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) -{ - // TODO: implement -} - -void ImageScaled::adjustPalette(const ColorFilter &shifter, uint32_t colorsToSkipMask) -{ - // TODO: implement -} - -void ImageScaled::prepareImages() -{ - switch(blitMode) - { - case EImageBlitMode::OPAQUE: - case EImageBlitMode::COLORKEY: - case EImageBlitMode::SIMPLE: - locator.layer = blitMode; - locator.playerColored = playerColor; - body = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage(); - break; - - case EImageBlitMode::WITH_SHADOW_AND_OVERLAY: - case EImageBlitMode::ONLY_BODY: - locator.layer = EImageBlitMode::ONLY_BODY; - locator.playerColored = playerColor; - body = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage(); - break; - - case EImageBlitMode::WITH_SHADOW: - case EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY: - locator.layer = EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY; - locator.playerColored = playerColor; - body = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage(); - break; - - case EImageBlitMode::ONLY_SHADOW: - case EImageBlitMode::ONLY_OVERLAY: - body = nullptr; - break; - } - - switch(blitMode) - { - case EImageBlitMode::WITH_SHADOW: - case EImageBlitMode::ONLY_SHADOW: - case EImageBlitMode::WITH_SHADOW_AND_OVERLAY: - locator.layer = EImageBlitMode::ONLY_SHADOW; - locator.playerColored = PlayerColor::CANNOT_DETERMINE; - shadow = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage(); - break; - default: - shadow = nullptr; - break; - } - - switch(blitMode) - { - case EImageBlitMode::ONLY_OVERLAY: - case EImageBlitMode::WITH_SHADOW_AND_OVERLAY: - locator.layer = EImageBlitMode::ONLY_OVERLAY; - locator.playerColored = PlayerColor::CANNOT_DETERMINE; - overlay = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage(); - break; - default: - overlay = nullptr; - break; - } -} diff --git a/client/renderSDL/ImageScaled.h b/client/renderSDL/ImageScaled.h deleted file mode 100644 index 0d719b88d..000000000 --- a/client/renderSDL/ImageScaled.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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" -#include "../render/IRenderHandler.h" - -#include "../../lib/Color.h" -#include "../../lib/constants/EntityIdentifiers.h" - -struct SDL_Palette; - -class SDLImageShared; - -// Upscaled image with several mechanisms to emulate H3 palette effects -class ImageScaled final : public IImage -{ -private: - - /// Original unscaled image - std::shared_ptr source; - - /// Upscaled shadow of our image, may be null - std::shared_ptr shadow; - - /// Upscaled main part of our image, may be null - std::shared_ptr body; - - /// Upscaled overlay (player color, selection highlight) of our image, may be null - std::shared_ptr overlay; - - ImageLocator locator; - - ColorRGBA colorMultiplier; - PlayerColor playerColor = PlayerColor::CANNOT_DETERMINE; - - uint8_t alphaValue; - EImageBlitMode blitMode; - - void prepareImages(); -public: - ImageScaled(const ImageLocator & locator, const std::shared_ptr & source, EImageBlitMode mode); - - void scaleInteger(int factor) override; - void scaleTo(const Point & size) override; - void exportBitmap(const boost::filesystem::path & path) const override; - bool isTransparent(const Point & coords) const override; - Rect contentRect() 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 setOverlayColor(const ColorRGBA & color) override; - void playerColored(PlayerColor player) override; - void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; - void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; - - std::shared_ptr getSharedImage() const override; -}; diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index 551473b7b..c0d8936d9 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -11,7 +11,7 @@ #include "RenderHandler.h" #include "SDLImage.h" -#include "ImageScaled.h" +#include "ScalableImage.h" #include "FontChain.h" #include "../gui/CGuiHandler.h" @@ -22,6 +22,7 @@ #include "../render/ColorFilter.h" #include "../render/IScreenHandler.h" #include "../../lib/json/JsonUtils.h" +#include "../../lib/CThreadHelper.h" #include "../../lib/filesystem/Filesystem.h" #include "../../lib/VCMIDirs.h" @@ -55,60 +56,7 @@ std::shared_ptr RenderHandler::getAnimationFile(const AnimationPath & return result; } -std::optional RenderHandler::getPathForScaleFactor(const ResourcePath & path, const std::string & factor) -{ - if(path.getType() == EResType::IMAGE) - { - auto p = ImagePath::builtin(path.getName()); - if(CResourceHandler::get()->existsResource(p.addPrefix("SPRITES" + factor + "X/"))) - return std::optional(p.addPrefix("SPRITES" + factor + "X/")); - if(CResourceHandler::get()->existsResource(p.addPrefix("DATA" + factor + "X/"))) - return std::optional(p.addPrefix("DATA" + factor + "X/")); - } - else - { - auto p = AnimationPath::builtin(path.getName()); - auto pJson = p.toType(); - if(CResourceHandler::get()->existsResource(p.addPrefix("SPRITES" + factor + "X/"))) - return std::optional(p.addPrefix("SPRITES" + factor + "X/")); - if(CResourceHandler::get()->existsResource(pJson)) - return std::optional(p); - if(CResourceHandler::get()->existsResource(pJson.addPrefix("SPRITES" + factor + "X/"))) - return std::optional(p.addPrefix("SPRITES" + factor + "X/")); - } - - return std::nullopt; -} - -std::pair RenderHandler::getScalePath(const ResourcePath & p) -{ - auto path = p; - int scaleFactor = 1; - if(getScalingFactor() > 1) - { - std::vector factorsToCheck = {getScalingFactor(), 4, 3, 2}; - for(auto factorToCheck : factorsToCheck) - { - std::string name = boost::algorithm::to_upper_copy(p.getName()); - boost::replace_all(name, "SPRITES/", std::string("SPRITES") + std::to_string(factorToCheck) + std::string("X/")); - boost::replace_all(name, "DATA/", std::string("DATA") + std::to_string(factorToCheck) + std::string("X/")); - ResourcePath scaledPath = ImagePath::builtin(name); - if(p.getType() != EResType::IMAGE) - scaledPath = AnimationPath::builtin(name); - auto tmpPath = getPathForScaleFactor(scaledPath, std::to_string(factorToCheck)); - if(tmpPath) - { - path = *tmpPath; - scaleFactor = factorToCheck; - break; - } - } - } - - return std::pair(path, scaleFactor); -}; - -void RenderHandler::initFromJson(AnimationLayoutMap & source, const JsonNode & config) +void RenderHandler::initFromJson(AnimationLayoutMap & source, const JsonNode & config, EImageBlitMode mode) { std::string basepath; basepath = config["basepath"].String(); @@ -128,7 +76,7 @@ void RenderHandler::initFromJson(AnimationLayoutMap & source, const JsonNode & c JsonNode toAdd = frame; JsonUtils::inherit(toAdd, base); toAdd["file"].String() = basepath + frame.String(); - source[groupID].emplace_back(toAdd); + source[groupID].emplace_back(toAdd, mode); } } @@ -149,15 +97,26 @@ void RenderHandler::initFromJson(AnimationLayoutMap & source, const JsonNode & c if (toAdd.Struct().count("defFile")) toAdd["defFile"].String() = basepath + node["defFile"].String(); - source[group][frame] = ImageLocator(toAdd); + source[group][frame] = ImageLocator(toAdd, mode); } } -RenderHandler::AnimationLayoutMap & RenderHandler::getAnimationLayout(const AnimationPath & path) +RenderHandler::AnimationLayoutMap & RenderHandler::getAnimationLayout(const AnimationPath & path, int scalingFactor, EImageBlitMode mode) { - auto tmp = getScalePath(path); - auto animPath = AnimationPath::builtin(tmp.first.getName()); - AnimationPath actualPath = boost::starts_with(animPath.getName(), "SPRITES") ? animPath : animPath.addPrefix("SPRITES/"); + static constexpr std::array scaledSpritesPath = { + "", // 0x + "SPRITES/", + "SPRITES2X/", + "SPRITES3X/", + "SPRITES4X/", + }; + + std::string pathString = path.getName(); + + if (boost::starts_with(pathString, "SPRITES/")) + pathString = pathString.substr(std::string("SPRITES/").length()); + + AnimationPath actualPath = AnimationPath::builtin(scaledSpritesPath.at(scalingFactor) + pathString); auto it = animationLayouts.find(actualPath); @@ -184,15 +143,11 @@ RenderHandler::AnimationLayoutMap & RenderHandler::getAnimationLayout(const Anim std::unique_ptr textData(new ui8[stream->getSize()]); stream->read(textData.get(), stream->getSize()); - const JsonNode config(reinterpret_cast(textData.get()), stream->getSize(), animPath.getOriginalName()); + const JsonNode config(reinterpret_cast(textData.get()), stream->getSize(), path.getOriginalName()); - initFromJson(result, config); + initFromJson(result, config, mode); } - for(auto & g : result) - for(auto & i : g.second) - i.preScaledFactor = tmp.second; - animationLayouts[actualPath] = result; return animationLayouts[actualPath]; } @@ -202,209 +157,170 @@ int RenderHandler::getScalingFactor() const return GH.screenHandler().getScalingFactor(); } -ImageLocator RenderHandler::getLocatorForAnimationFrame(const AnimationPath & path, int frame, int group) +ImageLocator RenderHandler::getLocatorForAnimationFrame(const AnimationPath & path, int frame, int group, EImageBlitMode mode) { - const auto & layout = getAnimationLayout(path); + const auto & layout = getAnimationLayout(path, 1, mode); if (!layout.count(group)) - return ImageLocator(ImagePath::builtin("DEFAULT")); + return ImageLocator(ImagePath::builtin("DEFAULT"), mode); if (frame >= layout.at(group).size()) - return ImageLocator(ImagePath::builtin("DEFAULT")); + return ImageLocator(ImagePath::builtin("DEFAULT"), mode); const auto & locator = layout.at(group).at(frame); if (locator.image || locator.defFile) return locator; - return ImageLocator(path, frame, group); + return ImageLocator(path, frame, group, mode); } -std::shared_ptr RenderHandler::loadImageImpl(const ImageLocator & locator) +std::shared_ptr RenderHandler::loadImageImpl(const ImageLocator & locator) { auto it = imageFiles.find(locator); if (it != imageFiles.end()) return it->second; - // TODO: order should be different: - // 1) try to find correctly scaled image - // 2) if fails -> try to find correctly transformed - // 3) if also fails -> try to find image from correct file - // 4) load missing part of the sequence - // TODO: check whether (load -> transform -> scale) or (load -> scale -> transform) order should be used for proper loading of pre-scaled data - auto imageFromFile = loadImageFromFile(locator.copyFile()); - auto transformedImage = transformImage(locator.copyFileTransform(), imageFromFile); - auto scaledImage = scaleImage(locator.copyFileTransformScale(), transformedImage); + auto sdlImage = loadImageFromFileUncached(locator); + auto scaledImage = std::make_shared(locator, sdlImage); + storeCachedImage(locator, scaledImage); return scaledImage; } -std::shared_ptr RenderHandler::loadImageFromFileUncached(const ImageLocator & locator) +std::shared_ptr RenderHandler::loadImageFromFileUncached(const ImageLocator & locator) { if(locator.image) { // TODO: create EmptySharedImage class that will be instantiated if image does not exists or fails to load - return std::make_shared(*locator.image, locator.preScaledFactor); + return std::make_shared(*locator.image); } if(locator.defFile) { auto defFile = getAnimationFile(*locator.defFile); - int preScaledFactor = locator.preScaledFactor; - if(!defFile) // no prescale for this frame - { - auto tmpPath = (*locator.defFile).getName(); - boost::algorithm::replace_all(tmpPath, "SPRITES2X/", "SPRITES/"); - boost::algorithm::replace_all(tmpPath, "SPRITES3X/", "SPRITES/"); - boost::algorithm::replace_all(tmpPath, "SPRITES4X/", "SPRITES/"); - preScaledFactor = 1; - defFile = getAnimationFile(AnimationPath::builtin(tmpPath)); - } if(defFile->hasFrame(locator.defFrame, locator.defGroup)) - return std::make_shared(defFile.get(), locator.defFrame, locator.defGroup, preScaledFactor); + return std::make_shared(defFile.get(), locator.defFrame, locator.defGroup); else { logGlobal->error("Frame %d in group %d not found in file: %s", locator.defFrame, locator.defGroup, locator.defFile->getName().c_str()); - return std::make_shared(ImagePath::builtin("DEFAULT"), locator.preScaledFactor); + return std::make_shared(ImagePath::builtin("DEFAULT")); } } throw std::runtime_error("Invalid image locator received!"); } -void RenderHandler::storeCachedImage(const ImageLocator & locator, std::shared_ptr image) +void RenderHandler::storeCachedImage(const ImageLocator & locator, std::shared_ptr image) { imageFiles[locator] = image; - -#if 0 - const boost::filesystem::path outPath = VCMIDirs::get().userExtractedPath() / "imageCache" / (locator.toString() + ".png"); - boost::filesystem::path outDir = outPath; - outDir.remove_filename(); - boost::filesystem::create_directories(outDir); - image->exportBitmap(outPath , nullptr); -#endif } -std::shared_ptr RenderHandler::loadImageFromFile(const ImageLocator & locator) +std::shared_ptr RenderHandler::loadSingleImage(const ImageLocator & locator) { - if (imageFiles.count(locator)) - return imageFiles.at(locator); + assert(locator.scalingFactor != 0); - auto result = loadImageFromFileUncached(locator); - storeCachedImage(locator, result); - return result; + static constexpr std::array scaledDataPath = { + "", // 0x + "DATA/", + "DATA2X/", + "DATA3X/", + "DATA4X/", + }; + + static constexpr std::array scaledSpritesPath = { + "", // 0x + "SPRITES/", + "SPRITES2X/", + "SPRITES3X/", + "SPRITES4X/", + }; + + if(locator.image) + { + std::string imagePathString = locator.image->getName(); + + if(locator.layer == EImageBlitMode::ONLY_OVERLAY) + imagePathString += "-OVERLAY"; + if(locator.layer == EImageBlitMode::ONLY_SHADOW) + imagePathString += "-SHADOW"; + + auto imagePath = ImagePath::builtin(imagePathString); + auto imagePathSprites = ImagePath::builtin(imagePathString).addPrefix(scaledSpritesPath.at(locator.scalingFactor)); + auto imagePathData = ImagePath::builtin(imagePathString).addPrefix(scaledDataPath.at(locator.scalingFactor)); + + if(CResourceHandler::get()->existsResource(imagePathSprites)) + return std::make_shared(imagePathSprites); + + if(CResourceHandler::get()->existsResource(imagePathData)) + return std::make_shared(imagePathData); + + if(CResourceHandler::get()->existsResource(imagePath)) + return std::make_shared(imagePath); + } + + if(locator.defFile) + { + AnimationPath defFilePath = locator.defFile->addPrefix(scaledSpritesPath.at(locator.scalingFactor)); + auto defFile = getAnimationFile(defFilePath); + + if(defFile && defFile->hasFrame(locator.defFrame, locator.defGroup)) + { + return std::make_shared(defFile.get(), locator.defFrame, locator.defGroup); + } + } + + return nullptr; } -std::shared_ptr RenderHandler::transformImage(const ImageLocator & locator, std::shared_ptr image) -{ - if (imageFiles.count(locator)) - return imageFiles.at(locator); - - auto result = image; - - if (locator.verticalFlip) - result = result->verticalFlip(); - - if (locator.horizontalFlip) - result = result->horizontalFlip(); - - storeCachedImage(locator, result); - return result; -} - -std::shared_ptr RenderHandler::scaleImage(const ImageLocator & locator, std::shared_ptr image) -{ - if (imageFiles.count(locator)) - return imageFiles.at(locator); - - auto handle = image->createImageReference(locator.layer); - - assert(locator.scalingFactor != 1); // should be filtered-out before - if (locator.playerColored != PlayerColor::CANNOT_DETERMINE) - handle->playerColored(locator.playerColored); - - handle->scaleInteger(locator.scalingFactor); - - auto result = handle->getSharedImage(); - storeCachedImage(locator, result); - return result; -} - -std::shared_ptr RenderHandler::loadImage(const ImageLocator & locator, EImageBlitMode mode) +std::shared_ptr RenderHandler::loadImage(const ImageLocator & locator) { ImageLocator adjustedLocator = locator; - if(adjustedLocator.image) - { - std::string imgPath = (*adjustedLocator.image).getName(); - if(adjustedLocator.layer == EImageBlitMode::ONLY_OVERLAY) - imgPath += "-OVERLAY"; - if(adjustedLocator.layer == EImageBlitMode::ONLY_SHADOW) - imgPath += "-SHADOW"; - - if(CResourceHandler::get()->existsResource(ImagePath::builtin(imgPath)) || - CResourceHandler::get()->existsResource(ImagePath::builtin(imgPath).addPrefix("DATA/")) || - CResourceHandler::get()->existsResource(ImagePath::builtin(imgPath).addPrefix("SPRITES/"))) - adjustedLocator.image = ImagePath::builtin(imgPath); - } - - if(adjustedLocator.defFile && adjustedLocator.scalingFactor == 0) - { - auto tmp = getScalePath(*adjustedLocator.defFile); - adjustedLocator.defFile = AnimationPath::builtin(tmp.first.getName()); - adjustedLocator.preScaledFactor = tmp.second; - } - if(adjustedLocator.image && adjustedLocator.scalingFactor == 0) - { - auto tmp = getScalePath(*adjustedLocator.image); - adjustedLocator.image = ImagePath::builtin(tmp.first.getName()); - adjustedLocator.preScaledFactor = tmp.second; - } - - if (adjustedLocator.scalingFactor == 0 && getScalingFactor() != 1 ) - { - auto unscaledLocator = adjustedLocator; - auto scaledLocator = adjustedLocator; - - unscaledLocator.scalingFactor = 1; - scaledLocator.scalingFactor = getScalingFactor(); - auto unscaledImage = loadImageImpl(unscaledLocator); - - return std::make_shared(scaledLocator, unscaledImage, mode); - } + std::shared_ptr result; if (adjustedLocator.scalingFactor == 0) { auto scaledLocator = adjustedLocator; scaledLocator.scalingFactor = getScalingFactor(); - return loadImageImpl(scaledLocator)->createImageReference(mode); + result = loadImageImpl(scaledLocator)->createImageReference(); } else - return loadImageImpl(adjustedLocator)->createImageReference(mode); + result = loadImageImpl(adjustedLocator)->createImageReference(); + + if (locator.horizontalFlip) + result->horizontalFlip(); + if (locator.verticalFlip) + result->verticalFlip(); + + return result; } std::shared_ptr RenderHandler::loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode) { - auto tmp = getScalePath(path); - ImageLocator locator = getLocatorForAnimationFrame(AnimationPath::builtin(tmp.first.getName()), frame, group); - locator.preScaledFactor = tmp.second; - return loadImage(locator, mode); + ImageLocator locator = getLocatorForAnimationFrame(path, frame, group, mode); + return loadImage(locator); } std::shared_ptr RenderHandler::loadImage(const ImagePath & path, EImageBlitMode mode) { - ImageLocator locator(path); - return loadImage(locator, mode); + ImageLocator locator(path, mode); + return loadImage(locator); } std::shared_ptr RenderHandler::createImage(SDL_Surface * source) { - return std::make_shared(source)->createImageReference(EImageBlitMode::SIMPLE); + auto baseImage = std::make_shared(source); + SharedImageLocator locator; + locator.layer = EImageBlitMode::SIMPLE; + auto scalableImage = std::make_shared(locator, baseImage); + + return scalableImage->createImageReference(); } std::shared_ptr RenderHandler::loadAnimation(const AnimationPath & path, EImageBlitMode mode) { - return std::make_shared(path, getAnimationLayout(path), mode); + return std::make_shared(path, getAnimationLayout(path, 1, mode), mode); } void RenderHandler::addImageListEntries(const EntityService * service) @@ -416,7 +332,7 @@ void RenderHandler::addImageListEntries(const EntityService * service) if (imageName.empty()) return; - auto & layout = getAnimationLayout(AnimationPath::builtin("SPRITES/" + listName)); + auto & layout = getAnimationLayout(AnimationPath::builtin("SPRITES/" + listName), 1, EImageBlitMode::SIMPLE); JsonNode entry; entry["file"].String() = imageName; @@ -424,7 +340,7 @@ void RenderHandler::addImageListEntries(const EntityService * service) if (index >= layout[group].size()) layout[group].resize(index + 1); - layout[group][index] = ImageLocator(entry); + layout[group][index] = ImageLocator(entry, EImageBlitMode::SIMPLE); }); }); } diff --git a/client/renderSDL/RenderHandler.h b/client/renderSDL/RenderHandler.h index 43df617a1..522927a59 100644 --- a/client/renderSDL/RenderHandler.h +++ b/client/renderSDL/RenderHandler.h @@ -17,7 +17,7 @@ VCMI_LIB_NAMESPACE_END class CDefFile; class SDLImageShared; -class ISharedImage; +class ScalableImageShared; class RenderHandler : public IRenderHandler { @@ -25,28 +25,22 @@ class RenderHandler : public IRenderHandler std::map> animationFiles; std::map animationLayouts; - std::map> imageFiles; + std::map> imageFiles; std::map> fonts; std::shared_ptr getAnimationFile(const AnimationPath & path); - std::optional getPathForScaleFactor(const ResourcePath & path, const std::string & factor); - std::pair getScalePath(const ResourcePath & p); - AnimationLayoutMap & getAnimationLayout(const AnimationPath & path); - void initFromJson(AnimationLayoutMap & layout, const JsonNode & config); + AnimationLayoutMap & getAnimationLayout(const AnimationPath & path, int scalingFactor, EImageBlitMode mode); + void initFromJson(AnimationLayoutMap & layout, const JsonNode & config, EImageBlitMode mode); void addImageListEntry(size_t index, size_t group, const std::string & listName, const std::string & imageName); void addImageListEntries(const EntityService * service); - void storeCachedImage(const ImageLocator & locator, std::shared_ptr image); + void storeCachedImage(const ImageLocator & locator, std::shared_ptr image); - std::shared_ptr loadImageImpl(const ImageLocator & config); + std::shared_ptr loadImageImpl(const ImageLocator & config); - std::shared_ptr loadImageFromFileUncached(const ImageLocator & locator); - std::shared_ptr loadImageFromFile(const ImageLocator & locator); + std::shared_ptr loadImageFromFileUncached(const ImageLocator & locator); - std::shared_ptr transformImage(const ImageLocator & locator, std::shared_ptr image); - std::shared_ptr scaleImage(const ImageLocator & locator, std::shared_ptr image); - - ImageLocator getLocatorForAnimationFrame(const AnimationPath & path, int frame, int group); + ImageLocator getLocatorForAnimationFrame(const AnimationPath & path, int frame, int group, EImageBlitMode mode); int getScalingFactor() const; @@ -55,10 +49,12 @@ public: // IRenderHandler implementation void onLibraryLoadingFinished(const Services * services) override; - std::shared_ptr loadImage(const ImageLocator & locator, EImageBlitMode mode) override; + std::shared_ptr loadImage(const ImageLocator & locator) override; std::shared_ptr loadImage(const ImagePath & path, EImageBlitMode mode) override; std::shared_ptr loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode) override; + std::shared_ptr loadSingleImage(const ImageLocator & locator) override; + std::shared_ptr loadAnimation(const AnimationPath & path, EImageBlitMode mode) override; std::shared_ptr createImage(SDL_Surface * source) override; diff --git a/client/renderSDL/SDLImage.cpp b/client/renderSDL/SDLImage.cpp index 7ce9f2d91..cc15faba9 100644 --- a/client/renderSDL/SDLImage.cpp +++ b/client/renderSDL/SDLImage.cpp @@ -28,59 +28,6 @@ class SDLImageLoader; -//First 8 colors in def palette used for transparency -static constexpr std::array sourcePalette = {{ - {0, 255, 255, SDL_ALPHA_OPAQUE}, - {255, 150, 255, SDL_ALPHA_OPAQUE}, - {255, 100, 255, SDL_ALPHA_OPAQUE}, - {255, 50, 255, SDL_ALPHA_OPAQUE}, - {255, 0, 255, SDL_ALPHA_OPAQUE}, - {255, 255, 0, SDL_ALPHA_OPAQUE}, - {180, 0, 255, SDL_ALPHA_OPAQUE}, - {0, 255, 0, SDL_ALPHA_OPAQUE} -}}; - -static constexpr std::array targetPalette = {{ - {0, 0, 0, 0 }, // 0 - transparency ( used in most images ) - {0, 0, 0, 64 }, // 1 - shadow border ( used in battle, adventure map def's ) - {0, 0, 0, 64 }, // 2 - shadow border ( used in fog-of-war def's ) - {0, 0, 0, 128}, // 3 - shadow body ( used in fog-of-war def's ) - {0, 0, 0, 128}, // 4 - shadow body ( used in battle, adventure map def's ) - {0, 0, 0, 0 }, // 5 - selection / owner flag ( used in battle, adventure map def's ) - {0, 0, 0, 128}, // 6 - shadow body below selection ( used in battle def's ) - {0, 0, 0, 64 } // 7 - shadow border below selection ( used in battle def's ) -}}; - -static ui8 mixChannels(ui8 c1, ui8 c2, ui8 a1, ui8 a2) -{ - return c1*a1 / 256 + c2*a2*(255 - a1) / 256 / 256; -} - -static ColorRGBA addColors(const ColorRGBA & base, const ColorRGBA & over) -{ - return ColorRGBA( - mixChannels(over.r, base.r, over.a, base.a), - mixChannels(over.g, base.g, over.a, base.a), - mixChannels(over.b, base.b, over.a, base.a), - static_cast(over.a + base.a * (255 - over.a) / 256) - ); -} - -static bool colorsSimilar (const SDL_Color & lhs, const SDL_Color & rhs) -{ - // it seems that H3 does not requires exact match to replace colors -> (255, 103, 255) gets interpreted as shadow - // exact logic is not clear and requires extensive testing with image editing - // potential reason is that H3 uses 16-bit color format (565 RGB bits), meaning that 3 least significant bits are lost in red and blue component - static const int threshold = 8; - - int diffR = static_cast(lhs.r) - rhs.r; - int diffG = static_cast(lhs.g) - rhs.g; - int diffB = static_cast(lhs.b) - rhs.b; - int diffA = static_cast(lhs.a) - rhs.a; - - return std::abs(diffR) < threshold && std::abs(diffG) < threshold && std::abs(diffB) < threshold && std::abs(diffA) < threshold; -} - int IImage::width() const { return dimensions().x; @@ -91,12 +38,11 @@ int IImage::height() const return dimensions().y; } -SDLImageShared::SDLImageShared(const CDefFile * data, size_t frame, size_t group, int preScaleFactor) +SDLImageShared::SDLImageShared(const CDefFile * data, size_t frame, size_t group) : surf(nullptr), margins(0, 0), fullSize(0, 0), - originalPalette(nullptr), - preScaleFactor(preScaleFactor) + originalPalette(nullptr) { SDLImageLoader loader(this); data->loadFrame(frame, group, loader); @@ -104,12 +50,11 @@ SDLImageShared::SDLImageShared(const CDefFile * data, size_t frame, size_t group savePalette(); } -SDLImageShared::SDLImageShared(SDL_Surface * from, int preScaleFactor) +SDLImageShared::SDLImageShared(SDL_Surface * from) : surf(nullptr), margins(0, 0), fullSize(0, 0), - originalPalette(nullptr), - preScaleFactor(preScaleFactor) + originalPalette(nullptr) { surf = from; if (surf == nullptr) @@ -122,12 +67,11 @@ SDLImageShared::SDLImageShared(SDL_Surface * from, int preScaleFactor) fullSize.y = surf->h; } -SDLImageShared::SDLImageShared(const ImagePath & filename, int preScaleFactor) +SDLImageShared::SDLImageShared(const ImagePath & filename) : surf(nullptr), margins(0, 0), fullSize(0, 0), - originalPalette(nullptr), - preScaleFactor(preScaleFactor) + originalPalette(nullptr) { surf = BitmapHandler::loadBitmap(filename); @@ -146,7 +90,6 @@ SDLImageShared::SDLImageShared(const ImagePath & filename, int preScaleFactor) } } - void SDLImageShared::draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const { if (!surf) @@ -292,26 +235,20 @@ std::shared_ptr SDLImageShared::scaleInteger(int factor, SDL SDL_SetSurfacePalette(surf, palette); SDL_Surface * scaled = nullptr; - if(preScaleFactor == factor) - return shared_from_this(); - else if(preScaleFactor == 1) - { - // 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, (int) round((float)surf->w * factor / preScaleFactor), (int) round((float)surf->h * factor / preScaleFactor)); - auto ret = std::make_shared(scaled, preScaleFactor); + // 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); + + 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 / preScaleFactor); - ret->margins.y = (int) round((float)margins.y * factor / preScaleFactor); + ret->margins.x = (int) round((float)margins.x * factor); + ret->margins.y = (int) round((float)margins.y * factor); ret->optimizeSurface(); // erase our own reference @@ -340,7 +277,7 @@ std::shared_ptr SDLImageShared::scaleTo(const Point & size, else CSDL_Ext::setDefaultColorKey(scaled);//just in case - auto ret = std::make_shared(scaled, preScaleFactor); + auto ret = std::make_shared(scaled); ret->fullSize.x = (int) round((float)fullSize.x * scaleX); ret->fullSize.y = (int) round((float)fullSize.y * scaleY); @@ -369,11 +306,6 @@ void SDLImageShared::exportBitmap(const boost::filesystem::path& path, SDL_Palet SDL_SetSurfacePalette(surf, originalPalette); } -void SDLImageIndexed::playerColored(PlayerColor player) -{ - graphics->setPlayerPalette(currentPalette, player); -} - bool SDLImageShared::isTransparent(const Point & coords) const { if (surf) @@ -384,22 +316,21 @@ bool SDLImageShared::isTransparent(const Point & coords) const Rect SDLImageShared::contentRect() const { - auto tmpMargins = margins / preScaleFactor; - auto tmpSize = Point(surf->w, surf->h) / preScaleFactor; + auto tmpMargins = margins; + auto tmpSize = Point(surf->w, surf->h); return Rect(tmpMargins, tmpSize); } +const SDL_Palette * SDLImageShared::getPalette() const +{ + if (!surf) + return nullptr; + return surf->format->palette; +} + Point SDLImageShared::dimensions() const { - return fullSize / preScaleFactor; -} - -std::shared_ptr SDLImageShared::createImageReference(EImageBlitMode mode) const -{ - if (surf && surf->format->palette) - return std::make_shared(shared_from_this(), originalPalette, mode); - else - return std::make_shared(shared_from_this(), mode); + return fullSize; } std::shared_ptr SDLImageShared::horizontalFlip() const @@ -408,7 +339,7 @@ std::shared_ptr SDLImageShared::horizontalFlip() const return shared_from_this(); SDL_Surface * flipped = CSDL_Ext::horizontalFlip(surf); - auto ret = std::make_shared(flipped, preScaleFactor); + auto ret = std::make_shared(flipped); ret->fullSize = fullSize; ret->margins.x = margins.x; ret->margins.y = fullSize.y - surf->h - margins.y; @@ -423,7 +354,7 @@ std::shared_ptr SDLImageShared::verticalFlip() const return shared_from_this(); SDL_Surface * flipped = CSDL_Ext::verticalFlip(surf); - auto ret = std::make_shared(flipped, preScaleFactor); + auto ret = std::make_shared(flipped); ret->fullSize = fullSize; ret->margins.x = fullSize.x - surf->w - margins.x; ret->margins.y = margins.y; @@ -445,219 +376,8 @@ void SDLImageShared::savePalette() SDL_SetPaletteColors(originalPalette, surf->format->palette->colors, 0, surf->format->palette->ncolors); } -void SDLImageIndexed::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) -{ - std::vector shifterColors(colorsToMove); - - for(uint32_t i=0; icolors[firstColorID + i]; - - SDL_SetPaletteColors(currentPalette, shifterColors.data(), firstColorID, colorsToMove); -} - -void SDLImageIndexed::adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) -{ - // If shadow is enabled, following colors must be skipped unconditionally - if (blitMode == EImageBlitMode::WITH_SHADOW || blitMode == EImageBlitMode::WITH_SHADOW_AND_OVERLAY) - colorsToSkipMask |= (1 << 0) + (1 << 1) + (1 << 4); - - // Note: here we skip first colors in the palette that are predefined in H3 images - for(int i = 0; i < currentPalette->ncolors; i++) - { - if (i < std::size(sourcePalette) && colorsSimilar(sourcePalette[i], originalPalette->colors[i])) - continue; - - if(i < std::numeric_limits::digits && ((colorsToSkipMask >> i) & 1) == 1) - continue; - - currentPalette->colors[i] = CSDL_Ext::toSDL(shifter.shiftColor(CSDL_Ext::fromSDL(originalPalette->colors[i]))); - } -} - -SDLImageIndexed::SDLImageIndexed(const std::shared_ptr & image, SDL_Palette * originalPalette, EImageBlitMode mode) - :SDLImageBase::SDLImageBase(image, mode) - ,originalPalette(originalPalette) -{ - currentPalette = SDL_AllocPalette(originalPalette->ncolors); - SDL_SetPaletteColors(currentPalette, originalPalette->colors, 0, originalPalette->ncolors); - - preparePalette(); -} - -SDLImageIndexed::~SDLImageIndexed() -{ - SDL_FreePalette(currentPalette); -} - -void SDLImageIndexed::setShadowTransparency(float factor) -{ - ColorRGBA shadow50(0, 0, 0, 128 * factor); - ColorRGBA shadow25(0, 0, 0, 64 * factor); - - std::array colorsSDL = { - originalPalette->colors[0], - originalPalette->colors[1], - originalPalette->colors[2], - originalPalette->colors[3], - originalPalette->colors[4] - }; - - // seems to be used unconditionally - colorsSDL[0] = CSDL_Ext::toSDL(Colors::TRANSPARENCY); - colorsSDL[1] = CSDL_Ext::toSDL(shadow25); - colorsSDL[4] = CSDL_Ext::toSDL(shadow50); - - // seems to be used only if color matches - if (colorsSimilar(originalPalette->colors[2], sourcePalette[2])) - colorsSDL[2] = CSDL_Ext::toSDL(shadow25); - - if (colorsSimilar(originalPalette->colors[3], sourcePalette[3])) - colorsSDL[3] = CSDL_Ext::toSDL(shadow50); - - SDL_SetPaletteColors(currentPalette, colorsSDL.data(), 0, colorsSDL.size()); -} - -void SDLImageIndexed::setOverlayColor(const ColorRGBA & color) -{ - currentPalette->colors[5] = CSDL_Ext::toSDL(addColors(targetPalette[5], color)); - - for (int i : {6,7}) - { - if (colorsSimilar(originalPalette->colors[i], sourcePalette[i])) - currentPalette->colors[i] = CSDL_Ext::toSDL(addColors(targetPalette[i], color)); - } -} - -void SDLImageIndexed::preparePalette() -{ - switch(blitMode) - { - case EImageBlitMode::ONLY_SHADOW: - case EImageBlitMode::ONLY_OVERLAY: - adjustPalette(ColorFilter::genAlphaShifter(0), 0); - break; - } - - switch(blitMode) - { - case EImageBlitMode::SIMPLE: - case EImageBlitMode::WITH_SHADOW: - case EImageBlitMode::ONLY_SHADOW: - case EImageBlitMode::WITH_SHADOW_AND_OVERLAY: - setShadowTransparency(1.0); - break; - case EImageBlitMode::ONLY_BODY: - case EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY: - case EImageBlitMode::ONLY_OVERLAY: - setShadowTransparency(0.0); - break; - } - - switch(blitMode) - { - case EImageBlitMode::ONLY_OVERLAY: - case EImageBlitMode::WITH_SHADOW_AND_OVERLAY: - setOverlayColor(Colors::WHITE_TRUE); - break; - case EImageBlitMode::ONLY_SHADOW: - case EImageBlitMode::ONLY_BODY: - setOverlayColor(Colors::TRANSPARENCY); - break; - } -} - SDLImageShared::~SDLImageShared() { SDL_FreeSurface(surf); SDL_FreePalette(originalPalette); } - -SDLImageBase::SDLImageBase(const std::shared_ptr & image, EImageBlitMode mode) - :image(image) - , alphaValue(SDL_ALPHA_OPAQUE) - , blitMode(mode) -{} - -std::shared_ptr SDLImageBase::getSharedImage() const -{ - return image; -} - -void SDLImageRGB::draw(SDL_Surface * where, const Point & pos, const Rect * src) const -{ - image->draw(where, nullptr, pos, src, Colors::WHITE_TRUE, alphaValue, blitMode); -} - -void SDLImageIndexed::draw(SDL_Surface * where, const Point & pos, const Rect * src) const -{ - image->draw(where, currentPalette, pos, src, Colors::WHITE_TRUE, alphaValue, blitMode); -} - -void SDLImageIndexed::exportBitmap(const boost::filesystem::path & path) const -{ - image->exportBitmap(path, currentPalette); -} - -void SDLImageIndexed::scaleTo(const Point & size) -{ - image = image->scaleTo(size, currentPalette); -} - -void SDLImageRGB::scaleTo(const Point & size) -{ - image = image->scaleTo(size, nullptr); -} - -void SDLImageIndexed::scaleInteger(int factor) -{ - image = image->scaleInteger(factor, currentPalette, blitMode); -} - -void SDLImageRGB::scaleInteger(int factor) -{ - image = image->scaleInteger(factor, nullptr, blitMode); -} - -void SDLImageRGB::exportBitmap(const boost::filesystem::path & path) const -{ - image->exportBitmap(path, nullptr); -} - -bool SDLImageBase::isTransparent(const Point & coords) const -{ - return image->isTransparent(coords); -} - -Rect SDLImageBase::contentRect() const -{ - return image->contentRect(); -} - -Point SDLImageBase::dimensions() const -{ - return image->dimensions(); -} - -void SDLImageBase::setAlpha(uint8_t value) -{ - alphaValue = value; -} - -void SDLImageBase::setBlitMode(EImageBlitMode mode) -{ - blitMode = mode; -} - -void SDLImageRGB::setOverlayColor(const ColorRGBA & color) -{} - -void SDLImageRGB::playerColored(PlayerColor player) -{} - -void SDLImageRGB::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) -{} - -void SDLImageRGB::adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) -{} - - diff --git a/client/renderSDL/SDLImage.h b/client/renderSDL/SDLImage.h index b4a8038d3..aa7d7efe6 100644 --- a/client/renderSDL/SDLImage.h +++ b/client/renderSDL/SDLImage.h @@ -35,9 +35,6 @@ class SDLImageShared final : public ISharedImage, public std::enable_shared_from //total size including borders Point fullSize; - //pre scaled image - int preScaleFactor; - // Keep the original palette, in order to do color switching operation void savePalette(); @@ -45,11 +42,11 @@ class SDLImageShared final : public ISharedImage, public std::enable_shared_from public: //Load image from def file - SDLImageShared(const CDefFile *data, size_t frame, size_t group=0, int preScaleFactor=1); + SDLImageShared(const CDefFile *data, size_t frame, size_t group=0); //Load from bitmap file - SDLImageShared(const ImagePath & filename, int preScaleFactor=1); + SDLImageShared(const ImagePath & filename); //Create using existing surface, extraRef will increase refcount on SDL_Surface - SDLImageShared(SDL_Surface * from, int preScaleFactor=1); + SDLImageShared(SDL_Surface * from); ~SDLImageShared(); void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const override; @@ -58,7 +55,9 @@ public: Point dimensions() const override; bool isTransparent(const Point & coords) const override; Rect contentRect() const override; - [[nodiscard]] std::shared_ptr createImageReference(EImageBlitMode mode) const override; + + const SDL_Palette * getPalette() const override; + [[nodiscard]] std::shared_ptr horizontalFlip() const override; [[nodiscard]] std::shared_ptr verticalFlip() const override; [[nodiscard]] std::shared_ptr scaleInteger(int factor, SDL_Palette * palette, EImageBlitMode blitMode) const override; @@ -66,58 +65,3 @@ public: friend class SDLImageLoader; }; - -class SDLImageBase : public IImage, boost::noncopyable -{ -protected: - std::shared_ptr image; - - uint8_t alphaValue; - EImageBlitMode blitMode; - -public: - SDLImageBase(const std::shared_ptr & image, EImageBlitMode mode); - - bool isTransparent(const Point & coords) const override; - Rect contentRect() const override; - Point dimensions() const override; - void setAlpha(uint8_t value) override; - void setBlitMode(EImageBlitMode mode) override; - std::shared_ptr getSharedImage() const override; -}; - -class SDLImageIndexed final : public SDLImageBase -{ - SDL_Palette * currentPalette = nullptr; - SDL_Palette * originalPalette = nullptr; - - void setShadowTransparency(float factor); - void preparePalette(); -public: - SDLImageIndexed(const std::shared_ptr & image, SDL_Palette * palette, EImageBlitMode mode); - ~SDLImageIndexed(); - - void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override; - void setOverlayColor(const ColorRGBA & color) override; - void playerColored(PlayerColor player) override; - void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; - void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; - void scaleInteger(int factor) override; - void scaleTo(const Point & size) override; - void exportBitmap(const boost::filesystem::path & path) const override; -}; - -class SDLImageRGB final : public SDLImageBase -{ -public: - using SDLImageBase::SDLImageBase; - - void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override; - void setOverlayColor(const ColorRGBA & color) override; - void playerColored(PlayerColor player) override; - void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; - void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; - void scaleInteger(int factor) override; - void scaleTo(const Point & size) override; - void exportBitmap(const boost::filesystem::path & path) const override; -}; diff --git a/client/renderSDL/ScalableImage.cpp b/client/renderSDL/ScalableImage.cpp new file mode 100644 index 000000000..6067a720c --- /dev/null +++ b/client/renderSDL/ScalableImage.cpp @@ -0,0 +1,489 @@ +/* + * ScalableImage.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 "ScalableImage.h" + +#include "SDLImage.h" +#include "SDL_Extensions.h" + +#include "../gui/CGuiHandler.h" + +#include "../render/ColorFilter.h" +#include "../render/Colors.h" +#include "../render/Graphics.h" +#include "../render/IRenderHandler.h" +#include "../render/IScreenHandler.h" + +#include "../../lib/constants/EntityIdentifiers.h" + +#include + +//First 8 colors in def palette used for transparency +static constexpr std::array sourcePalette = {{ + {0, 255, 255, SDL_ALPHA_OPAQUE}, + {255, 150, 255, SDL_ALPHA_OPAQUE}, + {255, 100, 255, SDL_ALPHA_OPAQUE}, + {255, 50, 255, SDL_ALPHA_OPAQUE}, + {255, 0, 255, SDL_ALPHA_OPAQUE}, + {255, 255, 0, SDL_ALPHA_OPAQUE}, + {180, 0, 255, SDL_ALPHA_OPAQUE}, + {0, 255, 0, SDL_ALPHA_OPAQUE} +}}; + +static constexpr std::array targetPalette = {{ + {0, 0, 0, 0 }, // 0 - transparency ( used in most images ) + {0, 0, 0, 64 }, // 1 - shadow border ( used in battle, adventure map def's ) + {0, 0, 0, 64 }, // 2 - shadow border ( used in fog-of-war def's ) + {0, 0, 0, 128}, // 3 - shadow body ( used in fog-of-war def's ) + {0, 0, 0, 128}, // 4 - shadow body ( used in battle, adventure map def's ) + {0, 0, 0, 0 }, // 5 - selection / owner flag ( used in battle, adventure map def's ) + {0, 0, 0, 128}, // 6 - shadow body below selection ( used in battle def's ) + {0, 0, 0, 64 } // 7 - shadow border below selection ( used in battle def's ) +}}; + +static ui8 mixChannels(ui8 c1, ui8 c2, ui8 a1, ui8 a2) +{ + return c1*a1 / 256 + c2*a2*(255 - a1) / 256 / 256; +} + +static ColorRGBA addColors(const ColorRGBA & base, const ColorRGBA & over) +{ + return ColorRGBA( + mixChannels(over.r, base.r, over.a, base.a), + mixChannels(over.g, base.g, over.a, base.a), + mixChannels(over.b, base.b, over.a, base.a), + static_cast(over.a + base.a * (255 - over.a) / 256) + ); +} +static bool colorsSimilar (const SDL_Color & lhs, const SDL_Color & rhs) +{ + // it seems that H3 does not requires exact match to replace colors -> (255, 103, 255) gets interpreted as shadow + // exact logic is not clear and requires extensive testing with image editing + // potential reason is that H3 uses 16-bit color format (565 RGB bits), meaning that 3 least significant bits are lost in red and blue component + static const int threshold = 8; + + int diffR = static_cast(lhs.r) - rhs.r; + int diffG = static_cast(lhs.g) - rhs.g; + int diffB = static_cast(lhs.b) - rhs.b; + int diffA = static_cast(lhs.a) - rhs.a; + + return std::abs(diffR) < threshold && std::abs(diffG) < threshold && std::abs(diffB) < threshold && std::abs(diffA) < threshold; +} + +ScalableImageParameters::ScalableImageParameters(const SDL_Palette * originalPalette, EImageBlitMode blitMode) +{ + if (originalPalette) + { + palette = SDL_AllocPalette(originalPalette->ncolors); + SDL_SetPaletteColors(palette, originalPalette->colors, 0, originalPalette->ncolors); + preparePalette(originalPalette, blitMode); + } +} + +ScalableImageParameters::~ScalableImageParameters() +{ + SDL_FreePalette(palette); +} + +void ScalableImageParameters::preparePalette(const SDL_Palette * originalPalette, EImageBlitMode blitMode) +{ + switch(blitMode) + { + case EImageBlitMode::ONLY_SHADOW: + case EImageBlitMode::ONLY_OVERLAY: + adjustPalette(originalPalette, blitMode, ColorFilter::genAlphaShifter(0), 0); + break; + } + + switch(blitMode) + { + case EImageBlitMode::SIMPLE: + case EImageBlitMode::WITH_SHADOW: + case EImageBlitMode::ONLY_SHADOW: + case EImageBlitMode::WITH_SHADOW_AND_OVERLAY: + setShadowTransparency(originalPalette, 1.0); + break; + case EImageBlitMode::ONLY_BODY: + case EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY: + case EImageBlitMode::ONLY_OVERLAY: + setShadowTransparency(originalPalette, 0.0); + break; + } + + switch(blitMode) + { + case EImageBlitMode::ONLY_OVERLAY: + case EImageBlitMode::WITH_SHADOW_AND_OVERLAY: + setOverlayColor(originalPalette, Colors::WHITE_TRUE); + break; + case EImageBlitMode::ONLY_SHADOW: + case EImageBlitMode::ONLY_BODY: + setOverlayColor(originalPalette, Colors::TRANSPARENCY); + break; + } +} + +void ScalableImageParameters::setOverlayColor(const SDL_Palette * originalPalette, const ColorRGBA & color) +{ + palette->colors[5] = CSDL_Ext::toSDL(addColors(targetPalette[5], color)); + + for (int i : {6,7}) + { + if (colorsSimilar(originalPalette->colors[i], sourcePalette[i])) + palette->colors[i] = CSDL_Ext::toSDL(addColors(targetPalette[i], color)); + } +} + +void ScalableImageParameters::shiftPalette(const SDL_Palette * originalPalette, uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) +{ + std::vector shifterColors(colorsToMove); + + for(uint32_t i=0; icolors[firstColorID + i]; + + SDL_SetPaletteColors(palette, shifterColors.data(), firstColorID, colorsToMove); +} + +void ScalableImageParameters::setShadowTransparency(const SDL_Palette * originalPalette, float factor) +{ + ColorRGBA shadow50(0, 0, 0, 128 * factor); + ColorRGBA shadow25(0, 0, 0, 64 * factor); + + std::array colorsSDL = { + originalPalette->colors[0], + originalPalette->colors[1], + originalPalette->colors[2], + originalPalette->colors[3], + originalPalette->colors[4] + }; + + // seems to be used unconditionally + colorsSDL[0] = CSDL_Ext::toSDL(Colors::TRANSPARENCY); + colorsSDL[1] = CSDL_Ext::toSDL(shadow25); + colorsSDL[4] = CSDL_Ext::toSDL(shadow50); + + // seems to be used only if color matches + if (colorsSimilar(originalPalette->colors[2], sourcePalette[2])) + colorsSDL[2] = CSDL_Ext::toSDL(shadow25); + + if (colorsSimilar(originalPalette->colors[3], sourcePalette[3])) + colorsSDL[3] = CSDL_Ext::toSDL(shadow50); + + SDL_SetPaletteColors(palette, colorsSDL.data(), 0, colorsSDL.size()); +} + +void ScalableImageParameters::adjustPalette(const SDL_Palette * originalPalette, EImageBlitMode blitMode, const ColorFilter & shifter, uint32_t colorsToSkipMask) +{ + // If shadow is enabled, following colors must be skipped unconditionally + if (blitMode == EImageBlitMode::WITH_SHADOW || blitMode == EImageBlitMode::WITH_SHADOW_AND_OVERLAY) + colorsToSkipMask |= (1 << 0) + (1 << 1) + (1 << 4); + + // Note: here we skip first colors in the palette that are predefined in H3 images + for(int i = 0; i < palette->ncolors; i++) + { + if (i < std::size(sourcePalette) && colorsSimilar(sourcePalette[i], originalPalette->colors[i])) + continue; + + if(i < std::numeric_limits::digits && ((colorsToSkipMask >> i) & 1) == 1) + continue; + + palette->colors[i] = CSDL_Ext::toSDL(shifter.shiftColor(CSDL_Ext::fromSDL(originalPalette->colors[i]))); + } +} + +ScalableImageShared::ScalableImageShared(const SharedImageLocator & locator, const std::shared_ptr & baseImage) + :locator(locator) +{ + base[0] = baseImage; + assert(base[0] != nullptr); + + loadScaledImages(GH.screenHandler().getScalingFactor(), PlayerColor::CANNOT_DETERMINE); +} + +Point ScalableImageShared::dimensions() const +{ + return base[0]->dimensions(); +} + +void ScalableImageShared::exportBitmap(const boost::filesystem::path & path, const ScalableImageParameters & parameters) const +{ + base[0]->exportBitmap(path, parameters.palette); +} + +bool ScalableImageShared::isTransparent(const Point & coords) const +{ + return base[0]->isTransparent(coords); +} + +Rect ScalableImageShared::contentRect() const +{ + return base[0]->contentRect(); +} + +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){ + int index = 0; + if (parameters.flipVertical) + { + if (!images[index|1]) + images[index|1] = images[index]->verticalFlip(); + + index |= 1; + } + + if (parameters.flipHorizontal) + { + if (!images[index|2]) + images[index|2] = images[index]->horizontalFlip(); + + index |= 2; + } + + images[index]->draw(where, parameters.palette, dest, src, colorMultiplier, alphaValue, locator.layer); + }; + + if (scalingFactor == 1) + { + flipAndDraw(base, parameters.colorMultiplier, parameters.alphaValue); + } + else + { + if (scaled.at(scalingFactor).shadow.at(0)) + flipAndDraw(scaled.at(scalingFactor).shadow, Colors::WHITE_TRUE, parameters.alphaValue); + + if (parameters.player != PlayerColor::CANNOT_DETERMINE) + { + scaled.at(scalingFactor).playerColored[parameters.player.getNum()]->draw(where, parameters.palette, dest, src, Colors::WHITE_TRUE, parameters.alphaValue, locator.layer); + } + else + { + if (scaled.at(scalingFactor).body.at(0)) + flipAndDraw(scaled.at(scalingFactor).body, parameters.colorMultiplier, parameters.alphaValue); + } + + if (scaled.at(scalingFactor).overlay.at(0)) + flipAndDraw(scaled.at(scalingFactor).overlay, parameters.ovelayColorMultiplier, static_cast(parameters.alphaValue) * parameters.ovelayColorMultiplier.a / 255); + } +} + +const SDL_Palette * ScalableImageShared::getPalette() const +{ + return base[0]->getPalette(); +} + +std::shared_ptr ScalableImageShared::createImageReference() +{ + return std::make_shared(shared_from_this(), locator.layer); +} + +ScalableImageInstance::ScalableImageInstance(const std::shared_ptr & image, EImageBlitMode blitMode) + :image(image) + ,parameters(image->getPalette(), blitMode) + ,blitMode(blitMode) +{ + assert(image); +} + +void ScalableImageInstance::scaleTo(const Point & size) +{ + assert(0); +} + +void ScalableImageInstance::exportBitmap(const boost::filesystem::path & path) const +{ + image->exportBitmap(path, parameters); +} + +bool ScalableImageInstance::isTransparent(const Point & coords) const +{ + return image->isTransparent(coords); +} + +Rect ScalableImageInstance::contentRect() const +{ + return image->contentRect(); +} + +Point ScalableImageInstance::dimensions() const +{ + return image->dimensions(); +} + +void ScalableImageInstance::setAlpha(uint8_t value) +{ + parameters.alphaValue = value; +} + +void ScalableImageInstance::draw(SDL_Surface * where, const Point & pos, const Rect * src, int scalingFactor) const +{ + image->draw(where, pos, src, parameters, scalingFactor); +} + +void ScalableImageInstance::setOverlayColor(const ColorRGBA & color) +{ + parameters.ovelayColorMultiplier = color; + + if (parameters.palette) + parameters.setOverlayColor(image->getPalette(), color); +} + +void ScalableImageInstance::playerColored(PlayerColor player) +{ + parameters.player = player; + + if (!parameters.palette) + parameters.playerColored(player); + + image->preparePlayerColoredImage(player); +} + +void ScalableImageParameters::playerColored(PlayerColor player) +{ + graphics->setPlayerPalette(palette, player); +} + +void ScalableImageInstance::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) +{ + if (parameters.palette) + parameters.shiftPalette(image->getPalette(),firstColorID, colorsToMove, distanceToMove); +} + +void ScalableImageInstance::adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) +{ + if (parameters.palette) + parameters.adjustPalette(image->getPalette(), blitMode, shifter, colorsToSkipMask); +} + +void ScalableImageInstance::horizontalFlip() +{ + parameters.flipHorizontal = !parameters.flipHorizontal; +} + +void ScalableImageInstance::verticalFlip() +{ + parameters.flipVertical = !parameters.flipVertical; +} + +std::shared_ptr ScalableImageShared::loadOrGenerateImage(EImageBlitMode mode, int8_t scalingFactor, PlayerColor color) const +{ + ImageLocator loadingLocator; + + loadingLocator.image = locator.image; + loadingLocator.defFile = locator.defFile; + loadingLocator.defFrame = locator.defFrame; + loadingLocator.defGroup = locator.defGroup; + loadingLocator.layer = mode; + loadingLocator.scalingFactor = scalingFactor; + loadingLocator.playerColored = color; + + // best case - requested image is already available in filesystem + auto loadedImage = GH.renderHandler().loadSingleImage(loadingLocator); + if (loadedImage) + return loadedImage; + + // alternatively, find largest pre-scaled image, load it and rescale to desired scaling + Point targetSize = base[0]->dimensions() * scalingFactor; + for (int8_t scaling = 4; scaling > 1; --scaling) + { + loadingLocator.scalingFactor = scaling; + auto loadedImage = GH.renderHandler().loadSingleImage(loadingLocator); + if (loadedImage) + return loadedImage->scaleTo(targetSize, nullptr); + } + + // if all else fails - use base (presumably, indexed) image and convert it to desired form + ScalableImageParameters parameters(getPalette(), mode); + if (color != PlayerColor::CANNOT_DETERMINE) + parameters.playerColored(color); + return base[0]->scaleInteger(scalingFactor, parameters.palette, mode); +} + +void ScalableImageShared::loadScaledImages(int8_t scalingFactor, PlayerColor color) +{ + if (scalingFactor == 1) + return; // no op. TODO: consider loading 1x images for mods, as alternative to palette-based animations + + if (scaled[scalingFactor].body[0] == nullptr) + { + switch(locator.layer) + { + case EImageBlitMode::OPAQUE: + case EImageBlitMode::COLORKEY: + case EImageBlitMode::SIMPLE: + scaled[scalingFactor].body[0] = loadOrGenerateImage(locator.layer, scalingFactor, PlayerColor::CANNOT_DETERMINE); + break; + + case EImageBlitMode::WITH_SHADOW_AND_OVERLAY: + case EImageBlitMode::ONLY_BODY: + scaled[scalingFactor].body[0] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY, scalingFactor, PlayerColor::CANNOT_DETERMINE); + break; + + case EImageBlitMode::WITH_SHADOW: + case EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY: + scaled[scalingFactor].body[0] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY, scalingFactor, PlayerColor::CANNOT_DETERMINE); + break; + } + } + + if (color != PlayerColor::CANNOT_DETERMINE && scaled[scalingFactor].playerColored[color.getNum()] == nullptr) + { + switch(locator.layer) + { + case EImageBlitMode::OPAQUE: + case EImageBlitMode::COLORKEY: + case EImageBlitMode::SIMPLE: + scaled[scalingFactor].playerColored[color.getNum()] = loadOrGenerateImage(locator.layer, scalingFactor, color); + break; + + case EImageBlitMode::WITH_SHADOW_AND_OVERLAY: + case EImageBlitMode::ONLY_BODY: + scaled[scalingFactor].playerColored[color.getNum()] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY, scalingFactor, color); + break; + + case EImageBlitMode::WITH_SHADOW: + case EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY: + scaled[scalingFactor].playerColored[color.getNum()] = loadOrGenerateImage(EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY, scalingFactor, color); + break; + } + } + + if (scaled[scalingFactor].shadow[0] == nullptr) + { + switch(locator.layer) + { + case EImageBlitMode::WITH_SHADOW: + case EImageBlitMode::ONLY_SHADOW: + case EImageBlitMode::WITH_SHADOW_AND_OVERLAY: + scaled[scalingFactor].shadow[0] = loadOrGenerateImage(EImageBlitMode::ONLY_SHADOW, scalingFactor, PlayerColor::CANNOT_DETERMINE); + break; + default: + break; + } + } + + if (scaled[scalingFactor].overlay[0] == nullptr) + { + switch(locator.layer) + { + case EImageBlitMode::ONLY_OVERLAY: + case EImageBlitMode::WITH_SHADOW_AND_OVERLAY: + scaled[scalingFactor].overlay[0] = loadOrGenerateImage(EImageBlitMode::ONLY_OVERLAY, scalingFactor, PlayerColor::CANNOT_DETERMINE); + break; + default: + break; + } + } +} + +void ScalableImageShared::preparePlayerColoredImage(PlayerColor color) +{ + loadScaledImages(GH.screenHandler().getScalingFactor(), color); +} diff --git a/client/renderSDL/ScalableImage.h b/client/renderSDL/ScalableImage.h new file mode 100644 index 000000000..5d9e26a39 --- /dev/null +++ b/client/renderSDL/ScalableImage.h @@ -0,0 +1,124 @@ +/* + * ScalableImage.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 "../render/ImageLocator.h" +#include "../render/Colors.h" + +#include "../../lib/Color.h" + +struct SDL_Palette; + +class ScalableImageInstance; + +struct ScalableImageParameters : boost::noncopyable +{ + SDL_Palette * palette = nullptr; + + ColorRGBA colorMultiplier = Colors::WHITE_TRUE; + ColorRGBA ovelayColorMultiplier = Colors::WHITE_TRUE; + + PlayerColor player = PlayerColor::CANNOT_DETERMINE; + uint8_t alphaValue = 255; + + bool flipVertical = false; + bool flipHorizontal = false; + + ScalableImageParameters(const SDL_Palette * originalPalette, EImageBlitMode blitMode); + ~ScalableImageParameters(); + + void setShadowTransparency(const SDL_Palette * originalPalette, float factor); + void shiftPalette(const SDL_Palette * originalPalette, uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove); + void playerColored(PlayerColor player); + void setOverlayColor(const SDL_Palette * originalPalette, const ColorRGBA & color); + void preparePalette(const SDL_Palette * originalPalette, EImageBlitMode blitMode); + void adjustPalette(const SDL_Palette * originalPalette, EImageBlitMode blitMode, const ColorFilter & shifter, uint32_t colorsToSkipMask); +}; + +class ScalableImageShared final : public std::enable_shared_from_this, boost::noncopyable +{ + static constexpr int maxScaling = 4; + static constexpr int maxFlips = 4; + + using FlippedImages = std::array, maxFlips>; + using PlayerColoredImages = std::array, PlayerColor::PLAYER_LIMIT_I>; + + struct ScaledImage + { + /// Upscaled shadow of our image, may be null + FlippedImages shadow; + + /// Upscaled main part of our image, may be null + FlippedImages body; + + /// Upscaled overlay (player color, selection highlight) of our image, may be null + FlippedImages overlay; + + // player-colored images of this particular scale. These are never flipped in h3 + PlayerColoredImages playerColored; + }; + + // 1x image, usually indexed. Array of 4 images for every potential flip + FlippedImages base; + + // 1x-4x images. 1x versions are only used if dedicated image is present, othervice palette transform is applied to base image + std::array scaled; + + const SharedImageLocator locator; + + std::shared_ptr loadOrGenerateImage(EImageBlitMode mode, int8_t scalingFactor, PlayerColor color) const; + + void loadScaledImages(int8_t scalingFactor, PlayerColor color); + +public: + ScalableImageShared(const SharedImageLocator & locator, const std::shared_ptr & baseImage); + + Point dimensions() const; + void exportBitmap(const boost::filesystem::path & path, const ScalableImageParameters & parameters) const; + bool isTransparent(const Point & coords) const; + Rect contentRect() const; + void draw(SDL_Surface * where, const Point & dest, const Rect * src, const ScalableImageParameters & parameters, int scalingFactor); + + const SDL_Palette * getPalette() const; + + std::shared_ptr createImageReference(); + + void preparePlayerColoredImage(PlayerColor color); +}; + +class ScalableImageInstance final : public IImage +{ + friend class ScalableImageShared; + + std::shared_ptr image; + + ScalableImageParameters parameters; + EImageBlitMode blitMode; + +public: + ScalableImageInstance(const std::shared_ptr & image, EImageBlitMode blitMode); + + void scaleTo(const Point & size) override; + void exportBitmap(const boost::filesystem::path & path) const override; + bool isTransparent(const Point & coords) const override; + Rect contentRect() const override; + Point dimensions() const override; + void setAlpha(uint8_t value) override; + void draw(SDL_Surface * where, const Point & pos, const Rect * src, int scalingFactor) const override; + void setOverlayColor(const ColorRGBA & color) override; + void playerColored(PlayerColor player) override; + void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; + void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; + + void horizontalFlip(); + void verticalFlip(); +}; + diff --git a/client/widgets/Images.cpp b/client/widgets/Images.cpp index 4cbb6be1a..dfeab0602 100644 --- a/client/widgets/Images.cpp +++ b/client/widgets/Images.cpp @@ -53,8 +53,8 @@ CPicture::CPicture( const ImagePath & bmpname ) : CPicture(bmpname, Point(0,0)) {} -CPicture::CPicture( const ImagePath & bmpname, const Point & position ) - : bg(GH.renderHandler().loadImage(bmpname, EImageBlitMode::COLORKEY)) +CPicture::CPicture( const ImagePath & bmpname, const Point & position, EImageBlitMode mode ) + : bg(GH.renderHandler().loadImage(bmpname, mode)) , needRefresh(false) { pos.x += position.x; @@ -74,6 +74,10 @@ CPicture::CPicture( const ImagePath & bmpname, const Point & position ) addUsedEvents(SHOW_POPUP); } +CPicture::CPicture( const ImagePath & bmpname, const Point & position ) + :CPicture(bmpname, position, EImageBlitMode::COLORKEY) +{} + CPicture::CPicture(const ImagePath & bmpname, const Rect &SrcRect, int x, int y) : CPicture(bmpname, Point(x,y)) { diff --git a/client/widgets/Images.h b/client/widgets/Images.h index 80b100fc4..123bf3dac 100644 --- a/client/widgets/Images.h +++ b/client/widgets/Images.h @@ -21,6 +21,7 @@ class CAnimImage; class CLabel; class CAnimation; class IImage; +enum class EImageBlitMode : uint8_t; // Image class class CPicture : public CIntObject @@ -49,6 +50,7 @@ public: /// Loads image from specified file name CPicture(const ImagePath & bmpname); + CPicture(const ImagePath & bmpname, const Point & position, EImageBlitMode mode); CPicture(const ImagePath & bmpname, const Point & position); CPicture(const ImagePath & bmpname, int x, int y); diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 63fba04ea..b98f852ff 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -570,9 +570,8 @@ CCastleBuildings::CCastleBuildings(const CGTownInstance* Town): { OBJECT_CONSTRUCTION; - background = std::make_shared(town->getTown()->clientInfo.townBackground); + background = std::make_shared(town->getTown()->clientInfo.townBackground, Point(0,0), EImageBlitMode::OPAQUE); background->needRefresh = true; - background->getSurface()->setBlitMode(EImageBlitMode::OPAQUE); pos.w = background->pos.w; pos.h = background->pos.h; diff --git a/client/windows/CMapOverview.cpp b/client/windows/CMapOverview.cpp index c0845133d..0ab4aa794 100644 --- a/client/windows/CMapOverview.cpp +++ b/client/windows/CMapOverview.cpp @@ -139,9 +139,8 @@ std::shared_ptr CMapOverviewWidget::buildDrawMinimap(const JsonNode & double resize = maxSideLengthSrc / maxSideLengthDst; Point newMinimapSize = Point(minimapRect.w / resize, minimapRect.h / resize); - Canvas canvasScaled = Canvas(Point(rect.w, rect.h), CanvasScalingPolicy::AUTO); - canvasScaled.drawScaled(minimaps[id], Point((rect.w - newMinimapSize.x) / 2, (rect.h - newMinimapSize.y) / 2), newMinimapSize); - std::shared_ptr img = GH.renderHandler().createImage(canvasScaled.getInternalSurface()); + std::shared_ptr img = GH.renderHandler().createImage(minimaps[id].getInternalSurface()); + img->scaleTo(newMinimapSize); return std::make_shared(img, Point(rect.x, rect.y)); } diff --git a/client/windows/CWindowObject.cpp b/client/windows/CWindowObject.cpp index d60bf3a9d..58b4ec942 100644 --- a/client/windows/CWindowObject.cpp +++ b/client/windows/CWindowObject.cpp @@ -87,8 +87,7 @@ std::shared_ptr CWindowObject::createBg(const ImagePath & imageName, b if(imageName.empty()) return nullptr; - auto image = std::make_shared(imageName); - image->getSurface()->setBlitMode(EImageBlitMode::OPAQUE); + auto image = std::make_shared(imageName, Point(0,0), EImageBlitMode::OPAQUE); if(playerColored) image->setPlayerColor(LOCPLINT->playerID); return image; @@ -116,8 +115,7 @@ void CWindowObject::updateShadow() void CWindowObject::setShadow(bool on) { //size of shadow - int sizeOriginal = 8; - int size = sizeOriginal * GH.screenHandler().getScalingFactor(); + int size = 8; if(on == !shadowParts.empty()) return; @@ -182,9 +180,9 @@ void CWindowObject::setShadow(bool on) //FIXME: do something with this points Point shadowStart; if (options & BORDERED) - shadowStart = Point(sizeOriginal - 14, sizeOriginal - 14); + shadowStart = Point(size - 14, size - 14); else - shadowStart = Point(sizeOriginal, sizeOriginal); + shadowStart = Point(size, size); Point shadowPos; if (options & BORDERED) @@ -200,8 +198,8 @@ void CWindowObject::setShadow(bool on) //create base 8x8 piece of shadow SDL_Surface * shadowCorner = CSDL_Ext::copySurface(shadowCornerTempl); - SDL_Surface * shadowBottom = CSDL_Ext::scaleSurface(shadowBottomTempl, (fullsize.x - sizeOriginal) * GH.screenHandler().getScalingFactor(), size); - SDL_Surface * shadowRight = CSDL_Ext::scaleSurface(shadowRightTempl, size, (fullsize.y - sizeOriginal) * GH.screenHandler().getScalingFactor()); + SDL_Surface * shadowBottom = CSDL_Ext::scaleSurface(shadowBottomTempl, (fullsize.x - size), size); + SDL_Surface * shadowRight = CSDL_Ext::scaleSurface(shadowRightTempl, size, (fullsize.y - size)); blitAlphaCol(shadowBottom, 0); blitAlphaRow(shadowRight, 0);