From 11b437db62af8371fad482a473d23bd349ad3e7b Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sun, 3 Nov 2024 13:38:15 +0100 Subject: [PATCH 01/12] prescaled image support --- client/render/ImageLocator.cpp | 1 + client/render/ImageLocator.h | 1 + client/renderSDL/RenderHandler.cpp | 75 +++++++++++++++++++++++++++--- client/renderSDL/RenderHandler.h | 2 + client/renderSDL/SDLImage.cpp | 27 +++++++---- client/renderSDL/SDLImage.h | 7 ++- 6 files changed, 96 insertions(+), 17 deletions(-) diff --git a/client/render/ImageLocator.cpp b/client/render/ImageLocator.cpp index a9f732923..27c377d5b 100644 --- a/client/render/ImageLocator.cpp +++ b/client/render/ImageLocator.cpp @@ -71,6 +71,7 @@ ImageLocator ImageLocator::copyFile() const { ImageLocator result; result.scalingFactor = 1; + result.preScaledFactor = preScaledFactor; result.image = image; result.defFile = defFile; result.defFrame = defFrame; diff --git a/client/render/ImageLocator.h b/client/render/ImageLocator.h index e53679be0..f03150981 100644 --- a/client/render/ImageLocator.h +++ b/client/render/ImageLocator.h @@ -33,6 +33,7 @@ struct ImageLocator bool verticalFlip = false; bool horizontalFlip = false; int8_t scalingFactor = 0; // 0 = auto / use default scaling + int8_t preScaledFactor = 1; EImageLayer layer = EImageLayer::ALL; ImageLocator() = default; diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index f1afc8255..837d8035c 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -55,6 +55,58 @@ std::shared_ptr RenderHandler::getAnimationFile(const AnimationPath & return result; } +std::optional RenderHandler::getPath(ResourcePath path) +{ + if(CResourceHandler::get()->existsResource(path)) + return path; + if(path.getType() == EResType::IMAGE) + { + auto p = ImagePath::builtin(path.getName()); + if(CResourceHandler::get()->existsResource(p.addPrefix("DATA/"))) + return std::optional(p.addPrefix("DATA/")); + if(CResourceHandler::get()->existsResource(p.addPrefix("SPRITES/"))) + return std::optional(p.addPrefix("SPRITES/")); + } + else + { + auto p = AnimationPath::builtin(path.getName()); + auto pJson = p.toType(); + if(CResourceHandler::get()->existsResource(p.addPrefix("SPRITES/"))) + return std::optional(p.addPrefix("SPRITES/")); + if(CResourceHandler::get()->existsResource(pJson)) + return std::optional(p); + if(CResourceHandler::get()->existsResource(pJson.addPrefix("SPRITES/"))) + return std::optional(p.addPrefix("SPRITES/")); + } + + return std::nullopt; +} + +std::pair RenderHandler::getScalePath(ResourcePath p) +{ + auto path = p; + auto name = p.getName(); + int scaleFactor = 1; + if(getScalingFactor() > 1) + { + std::vector factorsToCheck = {getScalingFactor(), 4, 3, 2}; + for(auto factorToCheck : factorsToCheck) + { + ResourcePath scaledPath = ImagePath::builtin(name + "$" + std::to_string(factorToCheck)); + if(p.getType() != EResType::IMAGE) + scaledPath = AnimationPath::builtin(name + "$" + std::to_string(factorToCheck)); + if(getPath(scaledPath)) + { + path = scaledPath; + scaleFactor = factorToCheck; + break; + } + } + } + + return std::pair(path, scaleFactor); +}; + void RenderHandler::initFromJson(AnimationLayoutMap & source, const JsonNode & config) { std::string basepath; @@ -177,13 +229,13 @@ std::shared_ptr RenderHandler::loadImageFromFileUncached(const Ima 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); + return std::make_shared(*locator.image, locator.preScaledFactor); } if (locator.defFile) { auto defFile = getAnimationFile(*locator.defFile); - return std::make_shared(defFile.get(), locator.defFrame, locator.defGroup); + return std::make_shared(defFile.get(), locator.defFrame, locator.defGroup, locator.preScaledFactor); } throw std::runtime_error("Invalid image locator received!"); @@ -247,7 +299,7 @@ std::shared_ptr RenderHandler::scaleImage(const ImageLocator & loc if (locator.layer == EImageLayer::ALL && locator.playerColored != PlayerColor::CANNOT_DETERMINE) handle->playerColored(locator.playerColored); - handle->scaleInteger(locator.scalingFactor); + handle->scaleInteger(locator.scalingFactor); // TODO: try to optimize image size (possibly even before scaling?) - trim image borders if they are completely transparent auto result = handle->getSharedImage(); @@ -284,13 +336,17 @@ std::shared_ptr RenderHandler::loadImage(const ImageLocator & locator, E std::shared_ptr RenderHandler::loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode) { - ImageLocator locator = getLocatorForAnimationFrame(path, frame, group); + auto tmp = getScalePath(path); + ImageLocator locator = getLocatorForAnimationFrame(AnimationPath::builtin(tmp.first.getName()), frame, group); + locator.preScaledFactor = tmp.second; return loadImage(locator, mode); } std::shared_ptr RenderHandler::loadImage(const ImagePath & path, EImageBlitMode mode) { - ImageLocator locator(path); + auto tmp = getScalePath(path); + ImageLocator locator(ImagePath::builtin(tmp.first.getName())); + locator.preScaledFactor = tmp.second; return loadImage(locator, mode); } @@ -301,7 +357,14 @@ std::shared_ptr RenderHandler::createImage(SDL_Surface * source) std::shared_ptr RenderHandler::loadAnimation(const AnimationPath & path, EImageBlitMode mode) { - return std::make_shared(path, getAnimationLayout(path), mode); + auto tmp = getScalePath(path); + auto animPath = AnimationPath::builtin(tmp.first.getName()); + auto layout = getAnimationLayout(animPath); + for(auto & g : layout) + for(auto & i : g.second) + i.preScaledFactor = tmp.second; + + return std::make_shared(animPath, layout, mode); } void RenderHandler::addImageListEntries(const EntityService * service) diff --git a/client/renderSDL/RenderHandler.h b/client/renderSDL/RenderHandler.h index 20b13306a..e30ccbf4d 100644 --- a/client/renderSDL/RenderHandler.h +++ b/client/renderSDL/RenderHandler.h @@ -29,6 +29,8 @@ class RenderHandler : public IRenderHandler std::map> fonts; std::shared_ptr getAnimationFile(const AnimationPath & path); + std::optional getPath(ResourcePath path); + std::pair getScalePath(ResourcePath p); AnimationLayoutMap & getAnimationLayout(const AnimationPath & path); void initFromJson(AnimationLayoutMap & layout, const JsonNode & config); diff --git a/client/renderSDL/SDLImage.cpp b/client/renderSDL/SDLImage.cpp index 83067b8a2..61b1b2b52 100644 --- a/client/renderSDL/SDLImage.cpp +++ b/client/renderSDL/SDLImage.cpp @@ -89,11 +89,12 @@ int IImage::height() const return dimensions().y; } -SDLImageShared::SDLImageShared(const CDefFile * data, size_t frame, size_t group) +SDLImageShared::SDLImageShared(const CDefFile * data, size_t frame, size_t group, int scaleFactor) : surf(nullptr), margins(0, 0), fullSize(0, 0), - originalPalette(nullptr) + originalPalette(nullptr), + scaleFactor(scaleFactor) { SDLImageLoader loader(this); data->loadFrame(frame, group, loader); @@ -105,7 +106,8 @@ SDLImageShared::SDLImageShared(SDL_Surface * from) : surf(nullptr), margins(0, 0), fullSize(0, 0), - originalPalette(nullptr) + originalPalette(nullptr), + scaleFactor(1) { surf = from; if (surf == nullptr) @@ -118,11 +120,12 @@ SDLImageShared::SDLImageShared(SDL_Surface * from) fullSize.y = surf->h; } -SDLImageShared::SDLImageShared(const ImagePath & filename) +SDLImageShared::SDLImageShared(const ImagePath & filename, int scaleFactor) : surf(nullptr), margins(0, 0), fullSize(0, 0), - originalPalette(nullptr) + originalPalette(nullptr), + scaleFactor(scaleFactor) { surf = BitmapHandler::loadBitmap(filename); @@ -274,7 +277,13 @@ std::shared_ptr SDLImageShared::scaleInteger(int factor, SDL_Palet if (palette && surf && surf->format->palette) SDL_SetSurfacePalette(surf, palette); - SDL_Surface * scaled = CSDL_Ext::scaleSurfaceIntegerFactor(surf, factor, EScalingAlgorithm::XBRZ); + SDL_Surface * scaled = nullptr; + if(scaleFactor == factor) + scaled = CSDL_Ext::scaleSurfaceIntegerFactor(surf, 1, EScalingAlgorithm::NEAREST); // keep size + else if(scaleFactor == 1) + scaled = CSDL_Ext::scaleSurfaceIntegerFactor(surf, factor, EScalingAlgorithm::XBRZ); + else + scaled = CSDL_Ext::scaleSurface(surf, (surf->w / scaleFactor) * factor, (surf->h / scaleFactor) * factor); auto ret = std::make_shared(scaled); @@ -296,8 +305,8 @@ std::shared_ptr SDLImageShared::scaleInteger(int factor, SDL_Palet std::shared_ptr SDLImageShared::scaleTo(const Point & size, SDL_Palette * palette) const { - float scaleX = float(size.x) / dimensions().x; - float scaleY = float(size.y) / dimensions().y; + float scaleX = float(size.x) / fullSize.x; + float scaleY = float(size.y) / fullSize.y; if (palette && surf->format->palette) SDL_SetSurfacePalette(surf, palette); @@ -355,7 +364,7 @@ bool SDLImageShared::isTransparent(const Point & coords) const Point SDLImageShared::dimensions() const { - return fullSize; + return fullSize / scaleFactor; } std::shared_ptr SDLImageShared::createImageReference(EImageBlitMode mode) diff --git a/client/renderSDL/SDLImage.h b/client/renderSDL/SDLImage.h index e9465eda4..64e72703b 100644 --- a/client/renderSDL/SDLImage.h +++ b/client/renderSDL/SDLImage.h @@ -35,6 +35,9 @@ class SDLImageShared final : public ISharedImage, public std::enable_shared_from //total size including borders Point fullSize; + //pre scaled image + int scaleFactor; + // Keep the original palette, in order to do color switching operation void savePalette(); @@ -42,9 +45,9 @@ 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); + SDLImageShared(const CDefFile *data, size_t frame, size_t group=0, int scaleFactor=1); //Load from bitmap file - SDLImageShared(const ImagePath & filename); + SDLImageShared(const ImagePath & filename, int scaleFactor=1); //Create using existing surface, extraRef will increase refcount on SDL_Surface SDLImageShared(SDL_Surface * from); ~SDLImageShared(); From 52aa4aeb827d9084aacab32b06f0eb25aae248cc Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sun, 3 Nov 2024 22:17:29 +0100 Subject: [PATCH 02/12] fix for not loaded images --- client/renderSDL/RenderHandler.cpp | 32 ++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index 837d8035c..f4b8c1c68 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -299,7 +299,7 @@ std::shared_ptr RenderHandler::scaleImage(const ImageLocator & loc if (locator.layer == EImageLayer::ALL && locator.playerColored != PlayerColor::CANNOT_DETERMINE) handle->playerColored(locator.playerColored); - handle->scaleInteger(locator.scalingFactor); + handle->scaleInteger(locator.scalingFactor); // TODO: try to optimize image size (possibly even before scaling?) - trim image borders if they are completely transparent auto result = handle->getSharedImage(); @@ -309,10 +309,24 @@ std::shared_ptr RenderHandler::scaleImage(const ImageLocator & loc std::shared_ptr RenderHandler::loadImage(const ImageLocator & locator, EImageBlitMode mode) { - if (locator.scalingFactor == 0 && getScalingFactor() != 1 ) + ImageLocator loc = locator; + if(loc.defFile && loc.scalingFactor == 0 && loc.preScaledFactor == 1) { - auto unscaledLocator = locator; - auto scaledLocator = locator; + auto tmp = getScalePath(*loc.defFile); + loc.defFile = AnimationPath::builtin(tmp.first.getName()); + loc.preScaledFactor = tmp.second; + } + if(loc.image && loc.scalingFactor == 0 && loc.preScaledFactor == 1) + { + auto tmp = getScalePath(*loc.image); + loc.image = ImagePath::builtin(tmp.first.getName()); + loc.preScaledFactor = tmp.second; + } + + if (loc.scalingFactor == 0 && getScalingFactor() != 1 ) + { + auto unscaledLocator = loc; + auto scaledLocator = loc; unscaledLocator.scalingFactor = 1; scaledLocator.scalingFactor = getScalingFactor(); @@ -321,16 +335,16 @@ std::shared_ptr RenderHandler::loadImage(const ImageLocator & locator, E return std::make_shared(scaledLocator, unscaledImage, mode); } - if (locator.scalingFactor == 0) + if (loc.scalingFactor == 0) { - auto scaledLocator = locator; + auto scaledLocator = loc; scaledLocator.scalingFactor = getScalingFactor(); return loadImageImpl(scaledLocator)->createImageReference(mode); } else { - return loadImageImpl(locator)->createImageReference(mode); + return loadImageImpl(loc)->createImageReference(mode); } } @@ -344,9 +358,7 @@ std::shared_ptr RenderHandler::loadImage(const AnimationPath & path, int std::shared_ptr RenderHandler::loadImage(const ImagePath & path, EImageBlitMode mode) { - auto tmp = getScalePath(path); - ImageLocator locator(ImagePath::builtin(tmp.first.getName())); - locator.preScaledFactor = tmp.second; + ImageLocator locator(path); return loadImage(locator, mode); } From 7c3afde7eee38aab91a5f5d899f323d14e5ecac9 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Tue, 5 Nov 2024 23:12:02 +0100 Subject: [PATCH 03/12] fix --- client/renderSDL/RenderHandler.cpp | 31 +++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index f4b8c1c68..efc2c89e0 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -148,7 +148,9 @@ void RenderHandler::initFromJson(AnimationLayoutMap & source, const JsonNode & c RenderHandler::AnimationLayoutMap & RenderHandler::getAnimationLayout(const AnimationPath & path) { - AnimationPath actualPath = boost::starts_with(path.getName(), "SPRITES") ? path : path.addPrefix("SPRITES/"); + auto tmp = getScalePath(path); + auto animPath = AnimationPath::builtin(tmp.first.getName()); + AnimationPath actualPath = boost::starts_with(animPath.getName(), "SPRITES") ? animPath : animPath.addPrefix("SPRITES/"); auto it = animationLayouts.find(actualPath); @@ -175,11 +177,15 @@ 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(), path.getOriginalName()); + const JsonNode config(reinterpret_cast(textData.get()), stream->getSize(), animPath.getOriginalName()); initFromJson(result, config); } + for(auto & g : result) + for(auto & i : g.second) + i.preScaledFactor = tmp.second; + animationLayouts[actualPath] = result; return animationLayouts[actualPath]; } @@ -235,7 +241,17 @@ std::shared_ptr RenderHandler::loadImageFromFileUncached(const Ima if (locator.defFile) { auto defFile = getAnimationFile(*locator.defFile); - return std::make_shared(defFile.get(), locator.defFrame, locator.defGroup, locator.preScaledFactor); + int preScaledFactor = locator.preScaledFactor; + if(!defFile) // no presscale for this frame + { + auto tmpPath = (*locator.defFile).getName(); + boost::algorithm::replace_all(tmpPath, "$2", ""); + boost::algorithm::replace_all(tmpPath, "$3", ""); + boost::algorithm::replace_all(tmpPath, "$4", ""); + preScaledFactor = 1; + defFile = getAnimationFile(AnimationPath::builtin(tmpPath)); + } + return std::make_shared(defFile.get(), locator.defFrame, locator.defGroup, preScaledFactor); } throw std::runtime_error("Invalid image locator received!"); @@ -369,14 +385,7 @@ std::shared_ptr RenderHandler::createImage(SDL_Surface * source) std::shared_ptr RenderHandler::loadAnimation(const AnimationPath & path, EImageBlitMode mode) { - auto tmp = getScalePath(path); - auto animPath = AnimationPath::builtin(tmp.first.getName()); - auto layout = getAnimationLayout(animPath); - for(auto & g : layout) - for(auto & i : g.second) - i.preScaledFactor = tmp.second; - - return std::make_shared(animPath, layout, mode); + return std::make_shared(path, getAnimationLayout(path), mode); } void RenderHandler::addImageListEntries(const EntityService * service) From bcdef11093f7fa52565c96d96f6c262be9130493 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Thu, 7 Nov 2024 02:32:06 +0100 Subject: [PATCH 04/12] rename variable --- client/renderSDL/SDLImage.cpp | 18 +++++++++--------- client/renderSDL/SDLImage.h | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/client/renderSDL/SDLImage.cpp b/client/renderSDL/SDLImage.cpp index 61b1b2b52..194c9d561 100644 --- a/client/renderSDL/SDLImage.cpp +++ b/client/renderSDL/SDLImage.cpp @@ -89,12 +89,12 @@ int IImage::height() const return dimensions().y; } -SDLImageShared::SDLImageShared(const CDefFile * data, size_t frame, size_t group, int scaleFactor) +SDLImageShared::SDLImageShared(const CDefFile * data, size_t frame, size_t group, int preScaleFactor) : surf(nullptr), margins(0, 0), fullSize(0, 0), originalPalette(nullptr), - scaleFactor(scaleFactor) + preScaleFactor(preScaleFactor) { SDLImageLoader loader(this); data->loadFrame(frame, group, loader); @@ -107,7 +107,7 @@ SDLImageShared::SDLImageShared(SDL_Surface * from) margins(0, 0), fullSize(0, 0), originalPalette(nullptr), - scaleFactor(1) + preScaleFactor(1) { surf = from; if (surf == nullptr) @@ -120,12 +120,12 @@ SDLImageShared::SDLImageShared(SDL_Surface * from) fullSize.y = surf->h; } -SDLImageShared::SDLImageShared(const ImagePath & filename, int scaleFactor) +SDLImageShared::SDLImageShared(const ImagePath & filename, int preScaleFactor) : surf(nullptr), margins(0, 0), fullSize(0, 0), originalPalette(nullptr), - scaleFactor(scaleFactor) + preScaleFactor(preScaleFactor) { surf = BitmapHandler::loadBitmap(filename); @@ -278,12 +278,12 @@ std::shared_ptr SDLImageShared::scaleInteger(int factor, SDL_Palet SDL_SetSurfacePalette(surf, palette); SDL_Surface * scaled = nullptr; - if(scaleFactor == factor) + if(preScaleFactor == factor) scaled = CSDL_Ext::scaleSurfaceIntegerFactor(surf, 1, EScalingAlgorithm::NEAREST); // keep size - else if(scaleFactor == 1) + else if(preScaleFactor == 1) scaled = CSDL_Ext::scaleSurfaceIntegerFactor(surf, factor, EScalingAlgorithm::XBRZ); else - scaled = CSDL_Ext::scaleSurface(surf, (surf->w / scaleFactor) * factor, (surf->h / scaleFactor) * factor); + scaled = CSDL_Ext::scaleSurface(surf, (surf->w / preScaleFactor) * factor, (surf->h / preScaleFactor) * factor); auto ret = std::make_shared(scaled); @@ -364,7 +364,7 @@ bool SDLImageShared::isTransparent(const Point & coords) const Point SDLImageShared::dimensions() const { - return fullSize / scaleFactor; + return fullSize / preScaleFactor; } std::shared_ptr SDLImageShared::createImageReference(EImageBlitMode mode) diff --git a/client/renderSDL/SDLImage.h b/client/renderSDL/SDLImage.h index 64e72703b..c18c01b10 100644 --- a/client/renderSDL/SDLImage.h +++ b/client/renderSDL/SDLImage.h @@ -36,7 +36,7 @@ class SDLImageShared final : public ISharedImage, public std::enable_shared_from Point fullSize; //pre scaled image - int scaleFactor; + int preScaleFactor; // Keep the original palette, in order to do color switching operation void savePalette(); @@ -45,9 +45,9 @@ 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 scaleFactor=1); + SDLImageShared(const CDefFile *data, size_t frame, size_t group=0, int preScaleFactor=1); //Load from bitmap file - SDLImageShared(const ImagePath & filename, int scaleFactor=1); + SDLImageShared(const ImagePath & filename, int preScaleFactor=1); //Create using existing surface, extraRef will increase refcount on SDL_Surface SDLImageShared(SDL_Surface * from); ~SDLImageShared(); From 1f7cec3ae382e3a31d77b7af4263469f3b31cae7 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Fri, 8 Nov 2024 00:09:17 +0100 Subject: [PATCH 05/12] missing prescalefactor --- client/renderSDL/SDLImage.cpp | 12 ++++++------ client/renderSDL/SDLImage.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/client/renderSDL/SDLImage.cpp b/client/renderSDL/SDLImage.cpp index 194c9d561..d4236436f 100644 --- a/client/renderSDL/SDLImage.cpp +++ b/client/renderSDL/SDLImage.cpp @@ -102,12 +102,12 @@ SDLImageShared::SDLImageShared(const CDefFile * data, size_t frame, size_t group savePalette(); } -SDLImageShared::SDLImageShared(SDL_Surface * from) +SDLImageShared::SDLImageShared(SDL_Surface * from, int preScaleFactor) : surf(nullptr), margins(0, 0), fullSize(0, 0), originalPalette(nullptr), - preScaleFactor(1) + preScaleFactor(preScaleFactor) { surf = from; if (surf == nullptr) @@ -285,7 +285,7 @@ std::shared_ptr SDLImageShared::scaleInteger(int factor, SDL_Palet else scaled = CSDL_Ext::scaleSurface(surf, (surf->w / preScaleFactor) * factor, (surf->h / preScaleFactor) * factor); - auto ret = std::make_shared(scaled); + auto ret = std::make_shared(scaled, preScaleFactor); ret->fullSize.x = fullSize.x * factor; ret->fullSize.y = fullSize.y * factor; @@ -320,7 +320,7 @@ std::shared_ptr SDLImageShared::scaleTo(const Point & size, SDL_Pa else CSDL_Ext::setDefaultColorKey(scaled);//just in case - auto ret = std::make_shared(scaled); + auto ret = std::make_shared(scaled, preScaleFactor); ret->fullSize.x = (int) round((float)fullSize.x * scaleX); ret->fullSize.y = (int) round((float)fullSize.y * scaleY); @@ -378,7 +378,7 @@ std::shared_ptr SDLImageShared::createImageReference(EImageBlitMode mode std::shared_ptr SDLImageShared::horizontalFlip() const { SDL_Surface * flipped = CSDL_Ext::horizontalFlip(surf); - auto ret = std::make_shared(flipped); + auto ret = std::make_shared(flipped, preScaleFactor); ret->fullSize = fullSize; ret->margins.x = margins.x; ret->margins.y = fullSize.y - surf->h - margins.y; @@ -390,7 +390,7 @@ std::shared_ptr SDLImageShared::horizontalFlip() const std::shared_ptr SDLImageShared::verticalFlip() const { SDL_Surface * flipped = CSDL_Ext::verticalFlip(surf); - auto ret = std::make_shared(flipped); + auto ret = std::make_shared(flipped, preScaleFactor); ret->fullSize = fullSize; ret->margins.x = fullSize.x - surf->w - margins.x; ret->margins.y = margins.y; diff --git a/client/renderSDL/SDLImage.h b/client/renderSDL/SDLImage.h index c18c01b10..b5dcfb57a 100644 --- a/client/renderSDL/SDLImage.h +++ b/client/renderSDL/SDLImage.h @@ -49,7 +49,7 @@ public: //Load from bitmap file SDLImageShared(const ImagePath & filename, int preScaleFactor=1); //Create using existing surface, extraRef will increase refcount on SDL_Surface - SDLImageShared(SDL_Surface * from); + SDLImageShared(SDL_Surface * from, int preScaleFactor=1); ~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; From ac31a946e66f9ab68e19ce641377a5290a4a87dc Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sat, 9 Nov 2024 13:07:15 +0100 Subject: [PATCH 06/12] use path instead of different filename --- client/renderSDL/RenderHandler.cpp | 39 ++++++++++++++++-------------- client/renderSDL/RenderHandler.h | 2 +- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index efc2c89e0..f67264745 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -55,28 +55,28 @@ std::shared_ptr RenderHandler::getAnimationFile(const AnimationPath & return result; } -std::optional RenderHandler::getPath(ResourcePath path) +std::optional RenderHandler::getPath(ResourcePath path, std::string factor) { if(CResourceHandler::get()->existsResource(path)) return path; if(path.getType() == EResType::IMAGE) { auto p = ImagePath::builtin(path.getName()); - if(CResourceHandler::get()->existsResource(p.addPrefix("DATA/"))) - return std::optional(p.addPrefix("DATA/")); - if(CResourceHandler::get()->existsResource(p.addPrefix("SPRITES/"))) - return std::optional(p.addPrefix("SPRITES/")); + if(CResourceHandler::get()->existsResource(p.addPrefix("DATA" + factor + "X/"))) + return std::optional(p.addPrefix("DATA" + factor + "X/")); + if(CResourceHandler::get()->existsResource(p.addPrefix("SPRITES" + factor + "X/"))) + return std::optional(p.addPrefix("SPRITES" + factor + "X/")); } else { auto p = AnimationPath::builtin(path.getName()); auto pJson = p.toType(); - if(CResourceHandler::get()->existsResource(p.addPrefix("SPRITES/"))) - return std::optional(p.addPrefix("SPRITES/")); + 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/"))) - return std::optional(p.addPrefix("SPRITES/")); + if(CResourceHandler::get()->existsResource(pJson.addPrefix("SPRITES" + factor + "X/"))) + return std::optional(p.addPrefix("SPRITES" + factor + "X/")); } return std::nullopt; @@ -85,19 +85,22 @@ std::optional RenderHandler::getPath(ResourcePath path) std::pair RenderHandler::getScalePath(ResourcePath p) { auto path = p; - auto name = p.getName(); int scaleFactor = 1; if(getScalingFactor() > 1) { std::vector factorsToCheck = {getScalingFactor(), 4, 3, 2}; for(auto factorToCheck : factorsToCheck) { - ResourcePath scaledPath = ImagePath::builtin(name + "$" + std::to_string(factorToCheck)); + 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 + "$" + std::to_string(factorToCheck)); - if(getPath(scaledPath)) + scaledPath = AnimationPath::builtin(name); + auto tmpPath = getPath(scaledPath, std::to_string(factorToCheck)); + if(tmpPath) { - path = scaledPath; + path = *tmpPath; scaleFactor = factorToCheck; break; } @@ -242,12 +245,12 @@ std::shared_ptr RenderHandler::loadImageFromFileUncached(const Ima { auto defFile = getAnimationFile(*locator.defFile); int preScaledFactor = locator.preScaledFactor; - if(!defFile) // no presscale for this frame + if(!defFile) // no prescale for this frame { auto tmpPath = (*locator.defFile).getName(); - boost::algorithm::replace_all(tmpPath, "$2", ""); - boost::algorithm::replace_all(tmpPath, "$3", ""); - boost::algorithm::replace_all(tmpPath, "$4", ""); + boost::algorithm::replace_all(tmpPath, "2X/", "/"); + boost::algorithm::replace_all(tmpPath, "3X/", "/"); + boost::algorithm::replace_all(tmpPath, "4X/", "/"); preScaledFactor = 1; defFile = getAnimationFile(AnimationPath::builtin(tmpPath)); } diff --git a/client/renderSDL/RenderHandler.h b/client/renderSDL/RenderHandler.h index e30ccbf4d..59f70561b 100644 --- a/client/renderSDL/RenderHandler.h +++ b/client/renderSDL/RenderHandler.h @@ -29,7 +29,7 @@ class RenderHandler : public IRenderHandler std::map> fonts; std::shared_ptr getAnimationFile(const AnimationPath & path); - std::optional getPath(ResourcePath path); + std::optional getPath(ResourcePath path, std::string factor); std::pair getScalePath(ResourcePath p); AnimationLayoutMap & getAnimationLayout(const AnimationPath & path); void initFromJson(AnimationLayoutMap & layout, const JsonNode & config); From 35e198078b146cbf7f45a4627cbe2a980ba3411e Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sat, 9 Nov 2024 14:02:09 +0100 Subject: [PATCH 07/12] cleanup logic --- client/renderSDL/RenderHandler.cpp | 10 ++++------ client/renderSDL/RenderHandler.h | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index f67264745..d28bf7cbf 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -55,17 +55,15 @@ std::shared_ptr RenderHandler::getAnimationFile(const AnimationPath & return result; } -std::optional RenderHandler::getPath(ResourcePath path, std::string factor) +std::optional RenderHandler::getPathForScaleFactor(ResourcePath path, std::string factor) { - if(CResourceHandler::get()->existsResource(path)) - return path; if(path.getType() == EResType::IMAGE) { auto p = ImagePath::builtin(path.getName()); - if(CResourceHandler::get()->existsResource(p.addPrefix("DATA" + factor + "X/"))) - return std::optional(p.addPrefix("DATA" + factor + "X/")); 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 { @@ -97,7 +95,7 @@ std::pair RenderHandler::getScalePath(ResourcePath p) ResourcePath scaledPath = ImagePath::builtin(name); if(p.getType() != EResType::IMAGE) scaledPath = AnimationPath::builtin(name); - auto tmpPath = getPath(scaledPath, std::to_string(factorToCheck)); + auto tmpPath = getPathForScaleFactor(scaledPath, std::to_string(factorToCheck)); if(tmpPath) { path = *tmpPath; diff --git a/client/renderSDL/RenderHandler.h b/client/renderSDL/RenderHandler.h index 59f70561b..7014251ed 100644 --- a/client/renderSDL/RenderHandler.h +++ b/client/renderSDL/RenderHandler.h @@ -29,7 +29,7 @@ class RenderHandler : public IRenderHandler std::map> fonts; std::shared_ptr getAnimationFile(const AnimationPath & path); - std::optional getPath(ResourcePath path, std::string factor); + std::optional getPathForScaleFactor(ResourcePath path, std::string factor); std::pair getScalePath(ResourcePath p); AnimationLayoutMap & getAnimationLayout(const AnimationPath & path); void initFromJson(AnimationLayoutMap & layout, const JsonNode & config); From 3872a3ea891d26ce807b2a1a4ccd238148f7b041 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sat, 9 Nov 2024 17:58:23 +0100 Subject: [PATCH 08/12] fix fallback scaling --- client/renderSDL/RenderHandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index d28bf7cbf..4c5e8477e 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -327,13 +327,13 @@ std::shared_ptr RenderHandler::scaleImage(const ImageLocator & loc std::shared_ptr RenderHandler::loadImage(const ImageLocator & locator, EImageBlitMode mode) { ImageLocator loc = locator; - if(loc.defFile && loc.scalingFactor == 0 && loc.preScaledFactor == 1) + if(loc.defFile && loc.scalingFactor == 0) { auto tmp = getScalePath(*loc.defFile); loc.defFile = AnimationPath::builtin(tmp.first.getName()); loc.preScaledFactor = tmp.second; } - if(loc.image && loc.scalingFactor == 0 && loc.preScaledFactor == 1) + if(loc.image && loc.scalingFactor == 0) { auto tmp = getScalePath(*loc.image); loc.image = ImagePath::builtin(tmp.first.getName()); From 23709ab217d1c07eb9bbb7c6744ee88834d66344 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sat, 9 Nov 2024 21:04:20 +0100 Subject: [PATCH 09/12] support loading layer images --- client/renderSDL/RenderHandler.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index 4c5e8477e..72076ab97 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -361,6 +361,18 @@ std::shared_ptr RenderHandler::loadImage(const ImageLocator & locator, E } else { + if(loc.image) + { + std::string imgPath = (*loc.image).getName(); + if(loc.layer == EImageLayer::OVERLAY) + imgPath += "-overlay"; + if(loc.layer == EImageLayer::SHADOW) + imgPath += "-shadow"; + + if(CResourceHandler::get()->existsResource(ImagePath::builtin(imgPath))) + loc.image = ImagePath::builtin(imgPath); + } + return loadImageImpl(loc)->createImageReference(mode); } } From a6c985c4f57860d2ee307ecfd1c8f5c3f46cee11 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Wed, 13 Nov 2024 23:10:41 +0100 Subject: [PATCH 10/12] code review --- client/renderSDL/RenderHandler.cpp | 6 +++--- client/renderSDL/SDLImage.cpp | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index 72076ab97..7e052eb11 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -246,9 +246,9 @@ std::shared_ptr RenderHandler::loadImageFromFileUncached(const Ima if(!defFile) // no prescale for this frame { auto tmpPath = (*locator.defFile).getName(); - boost::algorithm::replace_all(tmpPath, "2X/", "/"); - boost::algorithm::replace_all(tmpPath, "3X/", "/"); - boost::algorithm::replace_all(tmpPath, "4X/", "/"); + 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)); } diff --git a/client/renderSDL/SDLImage.cpp b/client/renderSDL/SDLImage.cpp index d4236436f..d74024a55 100644 --- a/client/renderSDL/SDLImage.cpp +++ b/client/renderSDL/SDLImage.cpp @@ -279,7 +279,10 @@ std::shared_ptr SDLImageShared::scaleInteger(int factor, SDL_Palet SDL_Surface * scaled = nullptr; if(preScaleFactor == factor) - scaled = CSDL_Ext::scaleSurfaceIntegerFactor(surf, 1, EScalingAlgorithm::NEAREST); // keep size + { + scaled = CSDL_Ext::newSurface(Point(surf->w, surf->h), surf); + SDL_BlitSurface(surf, nullptr, scaled, nullptr); + } else if(preScaleFactor == 1) scaled = CSDL_Ext::scaleSurfaceIntegerFactor(surf, factor, EScalingAlgorithm::XBRZ); else From 1e77d08d1af78ac9df0ea0a6a5f5d362d9a953f1 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Wed, 13 Nov 2024 23:49:57 +0100 Subject: [PATCH 11/12] docs for modder --- docs/modders/HD_Graphics.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 docs/modders/HD_Graphics.md diff --git a/docs/modders/HD_Graphics.md b/docs/modders/HD_Graphics.md new file mode 100644 index 000000000..1704f599f --- /dev/null +++ b/docs/modders/HD_Graphics.md @@ -0,0 +1,29 @@ +# HD Graphics + +It's possible to provide alternative HD-Graphics within mods. They will be used if any upscaling filter is activated. + +## Preconditions + +It's still necessary to add 1x graphics as before. HD graphics are seperate from usual graphics. This allows to partitially use HD for a few graphics in mod. And avoid handling huge graphics if upscaling isn't enabled. + +Currently following scaling factors are possible to use: 2x, 3x, 4x. You can also provide multiple of them (increases size of mod, but improves loading performance for player). It's recommend to provide 2x and 3x images. + +If user for example selects 3x resolution and only 2x exists in mod then the 2x images are upscaled to 3x (same for other combinations > 1x). + +## Mod + +For upscaled images you have to use following folders (next to `sprites` and `data` folders): +- `sprites2x`, `sprites3x`, `sprites4x` for sprites +- `data2x`, `data3x`, `data4x` for images + +The sprites should have the same name and folder structure as in `sprites` and `data` folder. All images that are missing in the upscaled folders are scaled with the selected upscaling filter instead of using prescaled images. + +### Shadows / Overlays + +It's also possible (but not necessary) to add HD shadows: Just place a image next to the normal upscaled image with the suffix `-shadow`. E.g. `TestImage.png` and `TestImage-shadow.png`. + +Same for overlays with `-overlay`. But overlays are **necessary** for some animation graphics. They will be colorized by VCMI. + +Currently needed for: +- flaggable adventure map objects (needs a transparent image with white flags on it) +- creature battle animations (needs a transparent image with white outline of creature for highlighting on mouse hover) From 149c6cd7782a8bccd9dd29940655da04fed1aa1c Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Fri, 15 Nov 2024 19:09:34 +0100 Subject: [PATCH 12/12] code review --- client/renderSDL/RenderHandler.cpp | 40 +++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/client/renderSDL/RenderHandler.cpp b/client/renderSDL/RenderHandler.cpp index 7e052eb11..e08821bd8 100644 --- a/client/renderSDL/RenderHandler.cpp +++ b/client/renderSDL/RenderHandler.cpp @@ -326,24 +326,24 @@ std::shared_ptr RenderHandler::scaleImage(const ImageLocator & loc std::shared_ptr RenderHandler::loadImage(const ImageLocator & locator, EImageBlitMode mode) { - ImageLocator loc = locator; - if(loc.defFile && loc.scalingFactor == 0) + ImageLocator adjustedLocator = locator; + if(adjustedLocator.defFile && adjustedLocator.scalingFactor == 0) { - auto tmp = getScalePath(*loc.defFile); - loc.defFile = AnimationPath::builtin(tmp.first.getName()); - loc.preScaledFactor = tmp.second; + auto tmp = getScalePath(*adjustedLocator.defFile); + adjustedLocator.defFile = AnimationPath::builtin(tmp.first.getName()); + adjustedLocator.preScaledFactor = tmp.second; } - if(loc.image && loc.scalingFactor == 0) + if(adjustedLocator.image && adjustedLocator.scalingFactor == 0) { - auto tmp = getScalePath(*loc.image); - loc.image = ImagePath::builtin(tmp.first.getName()); - loc.preScaledFactor = tmp.second; + auto tmp = getScalePath(*adjustedLocator.image); + adjustedLocator.image = ImagePath::builtin(tmp.first.getName()); + adjustedLocator.preScaledFactor = tmp.second; } - if (loc.scalingFactor == 0 && getScalingFactor() != 1 ) + if (adjustedLocator.scalingFactor == 0 && getScalingFactor() != 1 ) { - auto unscaledLocator = loc; - auto scaledLocator = loc; + auto unscaledLocator = adjustedLocator; + auto scaledLocator = adjustedLocator; unscaledLocator.scalingFactor = 1; scaledLocator.scalingFactor = getScalingFactor(); @@ -352,28 +352,28 @@ std::shared_ptr RenderHandler::loadImage(const ImageLocator & locator, E return std::make_shared(scaledLocator, unscaledImage, mode); } - if (loc.scalingFactor == 0) + if (adjustedLocator.scalingFactor == 0) { - auto scaledLocator = loc; + auto scaledLocator = adjustedLocator; scaledLocator.scalingFactor = getScalingFactor(); return loadImageImpl(scaledLocator)->createImageReference(mode); } else { - if(loc.image) + if(adjustedLocator.image) { - std::string imgPath = (*loc.image).getName(); - if(loc.layer == EImageLayer::OVERLAY) + std::string imgPath = (*adjustedLocator.image).getName(); + if(adjustedLocator.layer == EImageLayer::OVERLAY) imgPath += "-overlay"; - if(loc.layer == EImageLayer::SHADOW) + if(adjustedLocator.layer == EImageLayer::SHADOW) imgPath += "-shadow"; if(CResourceHandler::get()->existsResource(ImagePath::builtin(imgPath))) - loc.image = ImagePath::builtin(imgPath); + adjustedLocator.image = ImagePath::builtin(imgPath); } - return loadImageImpl(loc)->createImageReference(mode); + return loadImageImpl(adjustedLocator)->createImageReference(mode); } }