1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-10-31 00:07:39 +02:00

xBRZ-upscaled images now support common palette-transform effects:

- Player coloring
- Flag color for map
- Glue selection for combat
This commit is contained in:
Ivan Savenko
2024-07-25 10:38:48 +00:00
parent 2d12cecded
commit ffba847f40
18 changed files with 464 additions and 291 deletions

View File

@@ -323,33 +323,6 @@ static ColorRGBA genBorderColor(ui8 alpha, const ColorRGBA & base)
return ColorRGBA(base.r, base.g, base.b, ui8(base.a * alpha / 256)); return ColorRGBA(base.r, base.g, base.b, ui8(base.a * alpha / 256));
} }
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),
ui8(over.a + base.a * (255 - over.a) / 256)
);
}
void CreatureAnimation::genSpecialPalette(IImage::SpecialPalette & target)
{
target.resize(8);
target[0] = genShadow(0);
target[1] = genShadow(shadowAlpha / 2);
// colors 2 & 3 are not used in creatures
target[4] = genShadow(shadowAlpha);
target[5] = genBorderColor(getBorderStrength(elapsedTime), border);
target[6] = addColors(genShadow(shadowAlpha), genBorderColor(getBorderStrength(elapsedTime), border));
target[7] = addColors(genShadow(shadowAlpha / 2), genBorderColor(getBorderStrength(elapsedTime), border));
}
void CreatureAnimation::nextFrame(Canvas & canvas, const ColorFilter & shifter, bool facingRight) void CreatureAnimation::nextFrame(Canvas & canvas, const ColorFilter & shifter, bool facingRight)
{ {
ColorRGBA shadowTest = shifter.shiftColor(genShadow(128)); ColorRGBA shadowTest = shifter.shiftColor(genShadow(128));
@@ -366,11 +339,12 @@ void CreatureAnimation::nextFrame(Canvas & canvas, const ColorFilter & shifter,
if(image) if(image)
{ {
IImage::SpecialPalette SpecialPalette; image->setShadowEnabled(true);
genSpecialPalette(SpecialPalette); image->setOverlayEnabled(isIdle());
if (isIdle())
image->setOverlayColor(genBorderColor(getBorderStrength(elapsedTime), border));
image->setSpecialPalette(SpecialPalette, IImage::SPECIAL_PALETTE_MASK_CREATURES); image->adjustPalette(shifter, 0);
image->adjustPalette(shifter, IImage::SPECIAL_PALETTE_MASK_CREATURES);
canvas.draw(image, pos.topLeft(), Rect(0, 0, pos.w, pos.h)); canvas.draw(image, pos.topLeft(), Rect(0, 0, pos.w, pos.h));

View File

@@ -107,9 +107,7 @@ private:
void endAnimation(); void endAnimation();
void genSpecialPalette(IImage::SpecialPalette & target);
public: public:
/// function(s) that will be called when animation ends, after reset to 1st frame /// function(s) that will be called when animation ends, after reset to 1st frame
/// NOTE that these functions will be fired only once /// NOTE that these functions will be fired only once
CFunctionList<void()> onAnimationReset; CFunctionList<void()> onAnimationReset;

View File

@@ -21,6 +21,7 @@
#include "../render/IImage.h" #include "../render/IImage.h"
#include "../render/IRenderHandler.h" #include "../render/IRenderHandler.h"
#include "../render/Colors.h" #include "../render/Colors.h"
#include "../render/Graphics.h"
#include "../../CCallback.h" #include "../../CCallback.h"
@@ -477,23 +478,20 @@ void MapRendererObjects::renderImage(IMapRendererContext & context, Canvas & tar
return; return;
image->setAlpha(transparency); image->setAlpha(transparency);
image->setFlagColor(object->tempOwner); image->setShadowEnabled(true);
image->setOverlayEnabled(object->getOwner().isValidPlayer() || object->getOwner() == PlayerColor::NEUTRAL);
if (object->getOwner().isValidPlayer())
image->setOverlayColor(graphics->playerColors[object->getOwner().getNum()]);
if (object->getOwner() == PlayerColor::NEUTRAL)
image->setOverlayColor(graphics->neutralColor);
Point offsetPixels = context.objectImageOffset(object->id, coordinates); Point offsetPixels = context.objectImageOffset(object->id, coordinates);
if ( offsetPixels.x < image->dimensions().x && offsetPixels.y < image->dimensions().y) if ( offsetPixels.x < image->dimensions().x && offsetPixels.y < image->dimensions().y)
{ {
Point imagePos = image->dimensions() - offsetPixels - Point(32, 32); Point imagePos = image->dimensions() - offsetPixels - Point(32, 32);
//if (transparency == 255)
//{
// Canvas intermediate(Point(32,32));
// intermediate.enableTransparency(true);
// image->setBlitMode(EImageBlitMode::OPAQUE);
// intermediate.draw(image, Point(0, 0), Rect(imagePos, Point(32,32)));
// target.draw(intermediate, Point(0,0));
// return;
//}
target.draw(image, Point(0, 0), Rect(imagePos, Point(32,32))); target.draw(image, Point(0, 0), Rect(imagePos, Point(32,32)));
} }
} }

View File

@@ -23,7 +23,7 @@ class CAnimation;
class IImage; class IImage;
class Canvas; class Canvas;
class IMapRendererContext; class IMapRendererContext;
enum class EImageBlitMode; enum class EImageBlitMode : uint8_t;
class MapTileStorage class MapTileStorage
{ {

View File

@@ -13,6 +13,7 @@
#include "../gui/CGuiHandler.h" #include "../gui/CGuiHandler.h"
#include "../render/IImage.h" #include "../render/IImage.h"
#include "../render/IRenderHandler.h" #include "../render/IRenderHandler.h"
#include "../render/IScreenHandler.h"
#include "../../lib/filesystem/Filesystem.h" #include "../../lib/filesystem/Filesystem.h"
#include "../../lib/json/JsonUtils.h" #include "../../lib/json/JsonUtils.h"

View File

@@ -36,48 +36,10 @@ enum class DefType : uint32_t
* DefFile, class used for def loading * * DefFile, class used for def loading *
*************************************************************************/ *************************************************************************/
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<int>(lhs.r) - rhs.r;
int diffG = static_cast<int>(lhs.g) - rhs.g;
int diffB = static_cast<int>(lhs.b) - rhs.b;
int diffA = static_cast<int>(lhs.a) - rhs.a;
return std::abs(diffR) < threshold && std::abs(diffG) < threshold && std::abs(diffB) < threshold && std::abs(diffA) < threshold;
}
CDefFile::CDefFile(const AnimationPath & Name): CDefFile::CDefFile(const AnimationPath & Name):
data(nullptr), data(nullptr),
palette(nullptr) palette(nullptr)
{ {
//First 8 colors in def palette used for transparency
static const SDL_Color sourcePalette[8] = {
{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 const SDL_Color targetPalette[8] = {
{0, 0, 0, 0 }, // transparency ( used in most images )
{0, 0, 0, 64 }, // shadow border ( used in battle, adventure map def's )
{0, 0, 0, 64 }, // shadow border ( used in fog-of-war def's )
{0, 0, 0, 128}, // shadow body ( used in fog-of-war def's )
{0, 0, 0, 128}, // shadow body ( used in battle, adventure map def's )
{0, 0, 0, 0 }, // selection / owner flag ( used in battle, adventure map def's )
{0, 0, 0, 128}, // shadow body below selection ( used in battle def's )
{0, 0, 0, 64 } // shadow border below selection ( used in battle def's )
};
data = CResourceHandler::get()->load(Name)->readAll().first; data = CResourceHandler::get()->load(Name)->readAll().first;
palette = std::unique_ptr<SDL_Color[]>(new SDL_Color[256]); palette = std::unique_ptr<SDL_Color[]>(new SDL_Color[256]);
@@ -99,18 +61,6 @@ CDefFile::CDefFile(const AnimationPath & Name):
palette[i].a = SDL_ALPHA_OPAQUE; palette[i].a = SDL_ALPHA_OPAQUE;
} }
// these colors seems to be used unconditionally
palette[0] = targetPalette[0];
palette[1] = targetPalette[1];
palette[4] = targetPalette[4];
// rest of special colors are used only if their RGB values are close to H3
for (uint32_t i = 0; i < 8; ++i)
{
if (colorsSimilar(sourcePalette[i], palette[i]))
palette[i] = targetPalette[i];
}
for (ui32 i=0; i<totalBlocks; i++) for (ui32 i=0; i<totalBlocks; i++)
{ {
size_t blockID = read_le_u32(data.get() + it); size_t blockID = read_le_u32(data.get() + it);

View File

@@ -15,6 +15,7 @@
const ColorRGBA Colors::YELLOW = { 229, 215, 123, ColorRGBA::ALPHA_OPAQUE }; const ColorRGBA Colors::YELLOW = { 229, 215, 123, ColorRGBA::ALPHA_OPAQUE };
const ColorRGBA Colors::WHITE = { 255, 243, 222, ColorRGBA::ALPHA_OPAQUE }; const ColorRGBA Colors::WHITE = { 255, 243, 222, ColorRGBA::ALPHA_OPAQUE };
const ColorRGBA Colors::WHITE_TRUE = { 255, 255, 255, ColorRGBA::ALPHA_OPAQUE };
const ColorRGBA Colors::METALLIC_GOLD = { 173, 142, 66, ColorRGBA::ALPHA_OPAQUE }; const ColorRGBA Colors::METALLIC_GOLD = { 173, 142, 66, ColorRGBA::ALPHA_OPAQUE };
const ColorRGBA Colors::GREEN = { 0, 255, 0, ColorRGBA::ALPHA_OPAQUE }; const ColorRGBA Colors::GREEN = { 0, 255, 0, ColorRGBA::ALPHA_OPAQUE };
const ColorRGBA Colors::CYAN = { 0, 255, 255, ColorRGBA::ALPHA_OPAQUE }; const ColorRGBA Colors::CYAN = { 0, 255, 255, ColorRGBA::ALPHA_OPAQUE };

View File

@@ -23,6 +23,9 @@ public:
/** the standard h3 white color */ /** the standard h3 white color */
static const ColorRGBA WHITE; static const ColorRGBA WHITE;
/** actual 100% white color */
static const ColorRGBA WHITE_TRUE;
/** the metallic gold color used mostly as a border around buttons */ /** the metallic gold color used mostly as a border around buttons */
static const ColorRGBA METALLIC_GOLD; static const ColorRGBA METALLIC_GOLD;

View File

@@ -23,9 +23,10 @@ VCMI_LIB_NAMESPACE_END
struct SDL_Surface; struct SDL_Surface;
struct SDL_Palette; struct SDL_Palette;
class ColorFilter; class ColorFilter;
class ISharedImage;
/// Defines which blit method will be selected when image is used for rendering /// Defines which blit method will be selected when image is used for rendering
enum class EImageBlitMode enum class EImageBlitMode : uint8_t
{ {
/// Preferred for images that don't need any background /// Preferred for images that don't need any background
/// Indexed or RGBA: Image can have no transparency and can be only used as background /// Indexed or RGBA: Image can have no transparency and can be only used as background
@@ -41,15 +42,11 @@ enum class EImageBlitMode
ALPHA ALPHA
}; };
/* /// Base class for images for use in client code.
* Base class for images, can be used for non-animation pictures as well /// This class represents current state of image, with potential transformations applied, such as player coloring
*/
class IImage class IImage
{ {
public: public:
using SpecialPalette = std::vector<ColorRGBA>;
static constexpr int32_t SPECIAL_PALETTE_MASK_CREATURES = 0b11110011;
//draws image on surface "where" at position //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 = nullptr) const = 0;
@@ -60,9 +57,6 @@ public:
//Change palette to specific player //Change palette to specific player
virtual void playerColored(PlayerColor player) = 0; virtual void playerColored(PlayerColor player) = 0;
//set special color for flag
virtual void setFlagColor(PlayerColor player) = 0;
//test transparency of specific pixel //test transparency of specific pixel
virtual bool isTransparent(const Point & coords) const = 0; virtual bool isTransparent(const Point & coords) const = 0;
@@ -78,23 +72,32 @@ public:
virtual void setBlitMode(EImageBlitMode mode) = 0; virtual void setBlitMode(EImageBlitMode mode) = 0;
//only indexed bitmaps with 7 special colors //only indexed bitmaps with 7 special colors
virtual void setSpecialPalette(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) = 0; virtual void setOverlayColor(const ColorRGBA & color) = 0;
virtual void setShadowEnabled(bool on) = 0;
virtual void setBodyEnabled(bool on) = 0;
virtual void setOverlayEnabled(bool on) = 0;
virtual std::shared_ptr<ISharedImage> getSharedImage() const = 0;
virtual ~IImage() = default; virtual ~IImage() = default;
}; };
/// Base class for image data, mostly for internal use
/// Represents unmodified pixel data, usually loaded from file
/// This image can be shared between multiple image handlers (IImage instances)
class ISharedImage class ISharedImage
{ {
public: public:
virtual Point dimensions() const = 0; virtual Point dimensions() const = 0;
virtual void exportBitmap(const boost::filesystem::path & path) const = 0; virtual void exportBitmap(const boost::filesystem::path & path) const = 0;
virtual bool isTransparent(const Point & coords) const = 0; virtual bool isTransparent(const Point & coords) const = 0;
virtual void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, uint8_t alpha, EImageBlitMode mode) const = 0; virtual void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const = 0;
virtual std::shared_ptr<IImage> createImageReference(EImageBlitMode mode) = 0; virtual std::shared_ptr<IImage> createImageReference(EImageBlitMode mode) = 0;
virtual std::shared_ptr<ISharedImage> horizontalFlip() const = 0; virtual std::shared_ptr<ISharedImage> horizontalFlip() const = 0;
virtual std::shared_ptr<ISharedImage> verticalFlip() const = 0; virtual std::shared_ptr<ISharedImage> verticalFlip() const = 0;
virtual std::shared_ptr<ISharedImage> scaleFast(const Point & size, SDL_Palette * palette) const = 0;
virtual ~ISharedImage() = default; virtual ~ISharedImage() = default;

View File

@@ -19,7 +19,7 @@ struct SDL_Surface;
class IImage; class IImage;
class CAnimation; class CAnimation;
enum class EImageBlitMode; enum class EImageBlitMode : uint8_t;
class IRenderHandler : public boost::noncopyable class IRenderHandler : public boost::noncopyable
{ {

View File

@@ -10,6 +10,9 @@
#include "StdInc.h" #include "StdInc.h"
#include "ImageLocator.h" #include "ImageLocator.h"
#include "../gui/CGuiHandler.h"
#include "IScreenHandler.h"
#include "../../lib/json/JsonNode.h" #include "../../lib/json/JsonNode.h"
@@ -47,10 +50,46 @@ bool ImageLocator::operator<(const ImageLocator & other) const
return defFrame < other.defFrame; return defFrame < other.defFrame;
if(verticalFlip != other.verticalFlip) if(verticalFlip != other.verticalFlip)
return verticalFlip < other.verticalFlip; return verticalFlip < other.verticalFlip;
return horizontalFlip < other.horizontalFlip; 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(layerShadow != other.layerShadow)
return layerShadow < other.layerShadow;
if(layerBody != other.layerBody)
return layerBody < other.layerBody;
if (layerOverlay != other.layerOverlay)
return layerOverlay < other.layerOverlay;
return false;
} }
bool ImageLocator::empty() const bool ImageLocator::empty() const
{ {
return !image.has_value() && !defFile.has_value(); return !image.has_value() && !defFile.has_value();
} }
ImageLocator ImageLocator::copyFile() const
{
ImageLocator result;
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
}

View File

@@ -10,6 +10,7 @@
#pragma once #pragma once
#include "../../lib/filesystem/ResourcePath.h" #include "../../lib/filesystem/ResourcePath.h"
#include "../../lib/constants/EntityIdentifiers.h"
struct ImageLocator struct ImageLocator
{ {
@@ -20,6 +21,11 @@ struct ImageLocator
bool verticalFlip = false; bool verticalFlip = false;
bool horizontalFlip = false; bool horizontalFlip = false;
int8_t scalingFactor = 1;
PlayerColor playerColored = PlayerColor::CANNOT_DETERMINE;
bool layerShadow = false;
bool layerBody = true;
bool layerOverlay = false;
ImageLocator() = default; ImageLocator() = default;
ImageLocator(const AnimationPath & path, int frame, int group); ImageLocator(const AnimationPath & path, int frame, int group);
@@ -28,4 +34,8 @@ struct ImageLocator
bool operator < (const ImageLocator & other) const; bool operator < (const ImageLocator & other) const;
bool empty() const; bool empty() const;
ImageLocator copyFile() const;
ImageLocator copyFileTransform() const;
ImageLocator copyFileTransformScale() const;
}; };

View File

@@ -13,88 +13,49 @@
#include "SDLImage.h" #include "SDLImage.h"
#include "SDL_Extensions.h" #include "SDL_Extensions.h"
#include "../gui/CGuiHandler.h"
#include "../render/IScreenHandler.h"
#include "../render/Colors.h"
#include "../../lib/constants/EntityIdentifiers.h" #include "../../lib/constants/EntityIdentifiers.h"
#include <SDL_surface.h> #include <SDL_surface.h>
ImageSharedScaled::ImageSharedScaled(std::shared_ptr<SDLImageShared> sourceImage) ImageScaled::ImageScaled(const ImageLocator & inputLocator, const std::shared_ptr<ISharedImage> & source, EImageBlitMode mode)
:sourceImage(sourceImage) : source(source)
{ , locator(inputLocator)
scaledImage = sourceImage->scaleFast(sourceImage->dimensions() * getScalingFactor()); , colorMultiplier(Colors::WHITE_TRUE)
}
int ImageSharedScaled::getScalingFactor() const
{
return 2;
}
void ImageSharedScaled::draw(SDL_Surface *where, SDL_Palette * palette, const Point &dest, const Rect *src, uint8_t alpha, EImageBlitMode mode) const
{
scaledImage->draw(where, nullptr, dest, src, alpha, mode);
}
void ImageSharedScaled::exportBitmap(const boost::filesystem::path &path) const
{
sourceImage->exportBitmap(path);
}
Point ImageSharedScaled::dimensions() const
{
return sourceImage->dimensions();
}
bool ImageSharedScaled::isTransparent(const Point &coords) const
{
return sourceImage->isTransparent(coords);
}
std::shared_ptr<IImage> ImageSharedScaled::createImageReference(EImageBlitMode mode)
{
return std::make_shared<ImageScaled>(shared_from_this(), mode);
}
std::shared_ptr<ISharedImage> ImageSharedScaled::horizontalFlip() const
{
return std::make_shared<ImageSharedScaled>(std::dynamic_pointer_cast<SDLImageShared>(sourceImage->horizontalFlip()));
}
std::shared_ptr<ISharedImage> ImageSharedScaled::verticalFlip() const
{
return std::make_shared<ImageSharedScaled>(std::dynamic_pointer_cast<SDLImageShared>(sourceImage->verticalFlip()));
}
std::shared_ptr<ImageSharedScaled> ImageSharedScaled::scaleFast(const Point &size) const
{
return std::make_shared<ImageSharedScaled>(sourceImage->scaleFast(size));
}
///////////////////////////////////////////////////////////////////////////////////////////////////
ImageScaled::ImageScaled(const std::shared_ptr<ImageSharedScaled> &image, EImageBlitMode mode)
:image(image)
, alphaValue(SDL_ALPHA_OPAQUE) , alphaValue(SDL_ALPHA_OPAQUE)
, blitMode(mode) , blitMode(mode)
{ {
locator.scalingFactor = GH.screenHandler().getScalingFactor();
setBodyEnabled(true);
}
std::shared_ptr<ISharedImage> ImageScaled::getSharedImage() const
{
return body;
} }
void ImageScaled::scaleFast(const Point &size) void ImageScaled::scaleFast(const Point &size)
{ {
image = image->scaleFast(size); if (body)
body = body->scaleFast(size, nullptr); // FIXME: adjust for scaling
} }
void ImageScaled::exportBitmap(const boost::filesystem::path &path) const void ImageScaled::exportBitmap(const boost::filesystem::path &path) const
{ {
image->exportBitmap(path); source->exportBitmap(path);
} }
bool ImageScaled::isTransparent(const Point &coords) const bool ImageScaled::isTransparent(const Point &coords) const
{ {
return image->isTransparent(coords); return source->isTransparent(coords);
} }
Point ImageScaled::dimensions() const Point ImageScaled::dimensions() const
{ {
return image->dimensions(); return source->dimensions();
} }
void ImageScaled::setAlpha(uint8_t value) void ImageScaled::setAlpha(uint8_t value)
@@ -109,25 +70,75 @@ void ImageScaled::setBlitMode(EImageBlitMode mode)
void ImageScaled::draw(SDL_Surface *where, const Point &pos, const Rect *src) const void ImageScaled::draw(SDL_Surface *where, const Point &pos, const Rect *src) const
{ {
image->draw(where, pos, src, alphaValue, blitMode); 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::setSpecialPalette(const SpecialPalette &SpecialPalette, uint32_t colorsToSkipMask) void ImageScaled::setOverlayColor(const ColorRGBA & color)
{ {
colorMultiplier = color;
} }
void ImageScaled::playerColored(PlayerColor player) void ImageScaled::playerColored(PlayerColor player)
{ {
} playerColor = player;
if (body)
void ImageScaled::setFlagColor(PlayerColor player) setBodyEnabled(true); // regenerate
{
} }
void ImageScaled::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) void ImageScaled::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove)
{ {
// TODO: implement
} }
void ImageScaled::adjustPalette(const ColorFilter &shifter, uint32_t colorsToSkipMask) void ImageScaled::adjustPalette(const ColorFilter &shifter, uint32_t colorsToSkipMask)
{ {
// TODO: implement
}
void ImageScaled::setShadowEnabled(bool on)
{
if (on)
{
locator.layerBody = false;
locator.layerShadow = true;
locator.layerOverlay = false;
locator.playerColored = PlayerColor::CANNOT_DETERMINE;
shadow = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
}
else
shadow = nullptr;
}
void ImageScaled::setBodyEnabled(bool on)
{
if (on)
{
locator.layerBody = true;
locator.layerShadow = false;
locator.layerOverlay = false;
locator.playerColored = playerColor;
body = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
}
else
body = nullptr;
}
void ImageScaled::setOverlayEnabled(bool on)
{
if (on)
{
locator.layerBody = false;
locator.layerShadow = false;
locator.layerOverlay = true;
locator.playerColored = PlayerColor::CANNOT_DETERMINE;
overlay = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
}
else
overlay = nullptr;
} }

View File

@@ -12,40 +12,40 @@
#include "../render/IImage.h" #include "../render/IImage.h"
#include "../render/IRenderHandler.h" #include "../render/IRenderHandler.h"
#include "../../lib/Color.h"
#include "../../lib/constants/EntityIdentifiers.h"
struct SDL_Palette; struct SDL_Palette;
class SDLImageShared; class SDLImageShared;
class ImageSharedScaled final : public ISharedImage, public std::enable_shared_from_this<ImageSharedScaled>, boost::noncopyable // Upscaled image with several mechanisms to emulate H3 palette effects
{
std::shared_ptr<SDLImageShared> sourceImage;
std::shared_ptr<SDLImageShared> scaledImage;
int getScalingFactor() const;
public:
ImageSharedScaled(std::shared_ptr<SDLImageShared> sourceImage);
void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, uint8_t alpha, EImageBlitMode mode) const override;
void exportBitmap(const boost::filesystem::path & path) const override;
Point dimensions() const override;
bool isTransparent(const Point & coords) const override;
std::shared_ptr<IImage> createImageReference(EImageBlitMode mode) override;
std::shared_ptr<ISharedImage> horizontalFlip() const override;
std::shared_ptr<ISharedImage> verticalFlip() const override;
std::shared_ptr<ImageSharedScaled> scaleFast(const Point & size) const;
};
class ImageScaled final : public IImage class ImageScaled final : public IImage
{ {
private: private:
std::shared_ptr<ImageSharedScaled> image;
/// Original unscaled image
std::shared_ptr<ISharedImage> source;
/// Upscaled shadow of our image, may be null
std::shared_ptr<ISharedImage> shadow;
/// Upscaled main part of our image, may be null
std::shared_ptr<ISharedImage> body;
/// Upscaled overlay (player color, selection highlight) of our image, may be null
std::shared_ptr<ISharedImage> overlay;
ImageLocator locator;
ColorRGBA colorMultiplier;
PlayerColor playerColor = PlayerColor::CANNOT_DETERMINE;
uint8_t alphaValue; uint8_t alphaValue;
EImageBlitMode blitMode; EImageBlitMode blitMode;
public: public:
ImageScaled(const std::shared_ptr<ImageSharedScaled> & image, EImageBlitMode mode); ImageScaled(const ImageLocator & locator, const std::shared_ptr<ISharedImage> & source, EImageBlitMode mode);
void scaleFast(const Point & size) override; void scaleFast(const Point & size) override;
void exportBitmap(const boost::filesystem::path & path) const override; void exportBitmap(const boost::filesystem::path & path) const override;
@@ -54,9 +54,13 @@ public:
void setAlpha(uint8_t value) override; void setAlpha(uint8_t value) override;
void setBlitMode(EImageBlitMode mode) override; void setBlitMode(EImageBlitMode mode) override;
void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override; void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override;
void setSpecialPalette(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) override; void setOverlayColor(const ColorRGBA & color) override;
void playerColored(PlayerColor player) override; void playerColored(PlayerColor player) override;
void setFlagColor(PlayerColor player) override;
void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override;
void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override;
void setShadowEnabled(bool on) override;
void setBodyEnabled(bool on) override;
void setOverlayEnabled(bool on) override;
std::shared_ptr<ISharedImage> getSharedImage() const override;
}; };

View File

@@ -15,6 +15,8 @@
#include "../render/CAnimation.h" #include "../render/CAnimation.h"
#include "../render/CDefFile.h" #include "../render/CDefFile.h"
#include "../render/Colors.h"
#include "../render/ColorFilter.h"
#include "../../lib/json/JsonUtils.h" #include "../../lib/json/JsonUtils.h"
#include "../../lib/filesystem/Filesystem.h" #include "../../lib/filesystem/Filesystem.h"
@@ -131,47 +133,28 @@ int RenderHandler::getScalingFactor() const
return 2; return 2;
} }
std::shared_ptr<ISharedImage> RenderHandler::createScaledImage(std::shared_ptr<SDLImageShared> input) std::shared_ptr<IImage> RenderHandler::createImageReference(const ImageLocator & locator, std::shared_ptr<ISharedImage> input, EImageBlitMode mode)
{ {
if (getScalingFactor() == 1) if (getScalingFactor() == 1 || locator.scalingFactor != 1 || locator.empty())
return input; return input->createImageReference(mode);
else
return std::make_shared<ImageSharedScaled>(input); return std::make_shared<ImageScaled>(locator, input, mode);
} }
std::shared_ptr<ISharedImage> RenderHandler::loadImageFromSingleFile(const ImagePath & path) ImageLocator RenderHandler::getLocatorForAnimationFrame(const AnimationPath & path, int frame, int group)
{
auto result = createScaledImage(std::make_shared<SDLImageShared>(path));
imageFiles[ImageLocator(path)] = result;
return result;
}
std::shared_ptr<ISharedImage> RenderHandler::loadImageFromAnimationFileUncached(const AnimationPath & path, int frame, int group)
{ {
const auto & layout = getAnimationLayout(path); const auto & layout = getAnimationLayout(path);
if (!layout.count(group)) if (!layout.count(group))
return loadImageFromSingleFile(ImagePath::builtin("DEFAULT")); return ImageLocator(ImagePath::builtin("DEFAULT"));
if (frame >= layout.at(group).size()) if (frame >= layout.at(group).size())
return loadImageFromSingleFile(ImagePath::builtin("DEFAULT")); return ImageLocator(ImagePath::builtin("DEFAULT"));
const auto & locator = layout.at(group).at(frame); const auto & locator = layout.at(group).at(frame);
if (locator.image) if (locator.image || locator.defFile)
{ return locator;
return loadImageImpl(locator);
}
else
{
auto defFile = getAnimationFile(path);
return createScaledImage(std::make_shared<SDLImageShared>(defFile.get(), frame, group));
}
}
std::shared_ptr<ISharedImage> RenderHandler::loadImageFromAnimationFile(const AnimationPath & path, int frame, int group) return ImageLocator(path, frame, group);
{
auto result = loadImageFromAnimationFileUncached(path, frame, group);
imageFiles[ImageLocator(path, frame, group)] = result;
return result;
} }
std::shared_ptr<ISharedImage> RenderHandler::loadImageImpl(const ImageLocator & locator) std::shared_ptr<ISharedImage> RenderHandler::loadImageImpl(const ImageLocator & locator)
@@ -180,15 +163,52 @@ std::shared_ptr<ISharedImage> RenderHandler::loadImageImpl(const ImageLocator &
if (it != imageFiles.end()) if (it != imageFiles.end())
return it->second; return it->second;
std::shared_ptr<ISharedImage> result; // 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);
return scaledImage;
}
std::shared_ptr<ISharedImage> RenderHandler::loadImageFromFileUncached(const ImageLocator & locator)
{
if (locator.image) if (locator.image)
result = loadImageFromSingleFile(*locator.image); {
else if (locator.defFile) // TODO: create EmptySharedImage class that will be instantiated if image does not exists or fails to load
result = loadImageFromAnimationFile(*locator.defFile, locator.defFrame, locator.defGroup); return std::make_shared<SDLImageShared>(*locator.image);
}
if (!result) if (locator.defFile)
result = loadImageFromSingleFile(ImagePath::builtin("DEFAULT")); {
auto defFile = getAnimationFile(*locator.defFile);
return std::make_shared<SDLImageShared>(defFile.get(), locator.defFrame, locator.defGroup);
}
throw std::runtime_error("Invalid image locator received!");
}
std::shared_ptr<ISharedImage> RenderHandler::loadImageFromFile(const ImageLocator & locator)
{
if (imageFiles.count(locator))
return imageFiles.at(locator);
auto result = loadImageFromFileUncached(locator);
imageFiles[locator] = result;
return result;
}
std::shared_ptr<ISharedImage> RenderHandler::transformImage(const ImageLocator & locator, std::shared_ptr<ISharedImage> image)
{
if (imageFiles.count(locator))
return imageFiles.at(locator);
auto result = image;
if (locator.verticalFlip) if (locator.verticalFlip)
result = result->verticalFlip(); result = result->verticalFlip();
@@ -200,24 +220,50 @@ std::shared_ptr<ISharedImage> RenderHandler::loadImageImpl(const ImageLocator &
return result; return result;
} }
std::shared_ptr<ISharedImage> RenderHandler::scaleImage(const ImageLocator & locator, std::shared_ptr<ISharedImage> image)
{
if (imageFiles.count(locator))
return imageFiles.at(locator);
auto handle = image->createImageReference(EImageBlitMode::OPAQUE);
assert(locator.scalingFactor != 1); // should be filtered-out before
handle->setOverlayEnabled(locator.layerOverlay);
handle->setBodyEnabled(locator.layerBody);
handle->setShadowEnabled(locator.layerShadow);
if (locator.layerBody && locator.playerColored != PlayerColor::CANNOT_DETERMINE)
handle->playerColored(locator.playerColored);
handle->scaleFast(handle->dimensions() * locator.scalingFactor);
// TODO: try to optimize image size (possibly even before scaling?) - trim image borders if they are completely transparent
auto result = handle->getSharedImage();
imageFiles[locator] = result;
return result;
}
std::shared_ptr<IImage> RenderHandler::loadImage(const ImageLocator & locator, EImageBlitMode mode) std::shared_ptr<IImage> RenderHandler::loadImage(const ImageLocator & locator, EImageBlitMode mode)
{ {
return loadImageImpl(locator)->createImageReference(mode); return createImageReference(locator, loadImageImpl(locator), mode);
} }
std::shared_ptr<IImage> RenderHandler::loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode) std::shared_ptr<IImage> RenderHandler::loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode)
{ {
return loadImageFromAnimationFile(path, frame, group)->createImageReference(mode); auto locator = getLocatorForAnimationFrame(path, frame, group);
return loadImage(locator, mode);
} }
std::shared_ptr<IImage> RenderHandler::loadImage(const ImagePath & path, EImageBlitMode mode) std::shared_ptr<IImage> RenderHandler::loadImage(const ImagePath & path, EImageBlitMode mode)
{ {
return loadImageImpl(ImageLocator(path))->createImageReference(mode); return loadImage(ImageLocator(path), mode);
} }
std::shared_ptr<IImage> RenderHandler::createImage(SDL_Surface * source) std::shared_ptr<IImage> RenderHandler::createImage(SDL_Surface * source)
{ {
return std::make_shared<SDLImageShared>(source)->createImageReference(EImageBlitMode::ALPHA); return createImageReference(ImageLocator(), std::make_shared<SDLImageShared>(source), EImageBlitMode::ALPHA);
} }
std::shared_ptr<CAnimation> RenderHandler::loadAnimation(const AnimationPath & path, EImageBlitMode mode) std::shared_ptr<CAnimation> RenderHandler::loadAnimation(const AnimationPath & path, EImageBlitMode mode)

View File

@@ -34,14 +34,19 @@ class RenderHandler : public IRenderHandler
void addImageListEntry(size_t index, size_t group, const std::string & listName, const std::string & imageName); void addImageListEntry(size_t index, size_t group, const std::string & listName, const std::string & imageName);
void addImageListEntries(const EntityService * service); void addImageListEntries(const EntityService * service);
std::shared_ptr<ISharedImage> loadImageFromSingleFile(const ImagePath & path);
std::shared_ptr<ISharedImage> loadImageFromAnimationFileUncached(const AnimationPath & path, int frame, int group);
std::shared_ptr<ISharedImage> loadImageFromAnimationFile(const AnimationPath & path, int frame, int group);
std::shared_ptr<ISharedImage> loadImageImpl(const ImageLocator & config); std::shared_ptr<ISharedImage> loadImageImpl(const ImageLocator & config);
std::shared_ptr<ISharedImage> loadImageFromFileUncached(const ImageLocator & locator);
std::shared_ptr<ISharedImage> loadImageFromFile(const ImageLocator & locator);
std::shared_ptr<ISharedImage> transformImage(const ImageLocator & locator, std::shared_ptr<ISharedImage> image);
std::shared_ptr<ISharedImage> scaleImage(const ImageLocator & locator, std::shared_ptr<ISharedImage> image);
ImageLocator getLocatorForAnimationFrame(const AnimationPath & path, int frame, int group);
int getScalingFactor() const; int getScalingFactor() const;
std::shared_ptr<ISharedImage> createScaledImage(std::shared_ptr<SDLImageShared> input); std::shared_ptr<IImage> createImageReference(const ImageLocator & locator, std::shared_ptr<ISharedImage> input, EImageBlitMode mode);
public: public:
// IRenderHandler implementation // IRenderHandler implementation

View File

@@ -14,6 +14,7 @@
#include "SDL_Extensions.h" #include "SDL_Extensions.h"
#include "../render/ColorFilter.h" #include "../render/ColorFilter.h"
#include "../render/Colors.h"
#include "../render/CBitmapHandler.h" #include "../render/CBitmapHandler.h"
#include "../render/CDefFile.h" #include "../render/CDefFile.h"
#include "../render/Graphics.h" #include "../render/Graphics.h"
@@ -22,6 +23,59 @@
class SDLImageLoader; class SDLImageLoader;
//First 8 colors in def palette used for transparency
static const SDL_Color sourcePalette[8] = {
{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 const ColorRGBA targetPalette[8] = {
{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),
ui8(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<int>(lhs.r) - rhs.r;
int diffG = static_cast<int>(lhs.g) - rhs.g;
int diffB = static_cast<int>(lhs.b) - rhs.b;
int diffA = static_cast<int>(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 int IImage::width() const
{ {
return dimensions().x; return dimensions().x;
@@ -83,7 +137,7 @@ SDLImageShared::SDLImageShared(const ImagePath & filename)
} }
void SDLImageShared::draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, uint8_t alpha, EImageBlitMode mode) const 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) if (!surf)
return; return;
@@ -109,6 +163,7 @@ void SDLImageShared::draw(SDL_Surface * where, SDL_Palette * palette, const Poin
destShift += dest; destShift += dest;
SDL_SetSurfaceColorMod(surf, colorMultiplier.r, colorMultiplier.g, colorMultiplier.b);
SDL_SetSurfaceAlphaMod(surf, alpha); SDL_SetSurfaceAlphaMod(surf, alpha);
if (alpha != SDL_ALPHA_OPAQUE || (mode != EImageBlitMode::OPAQUE && surf->format->Amask != 0)) if (alpha != SDL_ALPHA_OPAQUE || (mode != EImageBlitMode::OPAQUE && surf->format->Amask != 0))
@@ -127,21 +182,19 @@ void SDLImageShared::draw(SDL_Surface * where, SDL_Palette * palette, const Poin
{ {
CSDL_Ext::blitSurface(surf, sourceRect, where, destShift); CSDL_Ext::blitSurface(surf, sourceRect, where, destShift);
} }
if (surf->format->palette)
SDL_SetSurfacePalette(surf, originalPalette);
} }
const SDL_Palette * SDLImageShared::getPalette() const std::shared_ptr<ISharedImage> SDLImageShared::scaleFast(const Point & size, SDL_Palette * palette) const
{
if (originalPalette == nullptr)
throw std::runtime_error("Palette not found!");
return originalPalette;
}
std::shared_ptr<SDLImageShared> SDLImageShared::scaleFast(const Point & size) const
{ {
float scaleX = float(size.x) / dimensions().x; float scaleX = float(size.x) / dimensions().x;
float scaleY = float(size.y) / dimensions().y; float scaleY = float(size.y) / dimensions().y;
if (palette && surf->format->palette)
SDL_SetSurfacePalette(surf, palette);
auto scaled = CSDL_Ext::scaleSurface(surf, (int)(surf->w * scaleX), (int)(surf->h * scaleY)); auto scaled = CSDL_Ext::scaleSurface(surf, (int)(surf->w * scaleX), (int)(surf->h * scaleY));
if (scaled->format && scaled->format->palette) // fix color keying, because SDL loses it at this point if (scaled->format && scaled->format->palette) // fix color keying, because SDL loses it at this point
@@ -162,6 +215,9 @@ std::shared_ptr<SDLImageShared> SDLImageShared::scaleFast(const Point & size) co
// erase our own reference // erase our own reference
SDL_FreeSurface(scaled); SDL_FreeSurface(scaled);
if (surf->format->palette)
SDL_SetSurfacePalette(surf, originalPalette);
return ret; return ret;
} }
@@ -175,12 +231,6 @@ void SDLImageIndexed::playerColored(PlayerColor player)
graphics->setPlayerPalette(currentPalette, player); graphics->setPlayerPalette(currentPalette, player);
} }
void SDLImageIndexed::setFlagColor(PlayerColor player)
{
if(player.isValidPlayer() || player==PlayerColor::NEUTRAL)
graphics->setPlayerFlagColor(currentPalette, player);
}
bool SDLImageShared::isTransparent(const Point & coords) const bool SDLImageShared::isTransparent(const Point & coords) const
{ {
if (surf) if (surf)
@@ -197,7 +247,7 @@ Point SDLImageShared::dimensions() const
std::shared_ptr<IImage> SDLImageShared::createImageReference(EImageBlitMode mode) std::shared_ptr<IImage> SDLImageShared::createImageReference(EImageBlitMode mode)
{ {
if (surf && surf->format->palette) if (surf && surf->format->palette)
return std::make_shared<SDLImageIndexed>(shared_from_this(), mode); return std::make_shared<SDLImageIndexed>(shared_from_this(), originalPalette, mode);
else else
return std::make_shared<SDLImageRGB>(shared_from_this(), mode); return std::make_shared<SDLImageRGB>(shared_from_this(), mode);
} }
@@ -241,8 +291,6 @@ void SDLImageShared::savePalette()
void SDLImageIndexed::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) void SDLImageIndexed::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove)
{ {
const SDL_Palette * originalPalette = image->getPalette();
std::vector<SDL_Color> shifterColors(colorsToMove); std::vector<SDL_Color> shifterColors(colorsToMove);
for(uint32_t i=0; i<colorsToMove; ++i) for(uint32_t i=0; i<colorsToMove; ++i)
@@ -253,11 +301,12 @@ void SDLImageIndexed::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove,
void SDLImageIndexed::adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) void SDLImageIndexed::adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask)
{ {
const SDL_Palette * originalPalette = image->getPalette();
// Note: here we skip first colors in the palette that are predefined in H3 images // Note: here we skip first colors in the palette that are predefined in H3 images
for(int i = 0; i < currentPalette->ncolors; i++) 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<uint32_t>::digits && ((colorsToSkipMask >> i) & 1) == 1) if(i < std::numeric_limits<uint32_t>::digits && ((colorsToSkipMask >> i) & 1) == 1)
continue; continue;
@@ -265,13 +314,16 @@ void SDLImageIndexed::adjustPalette(const ColorFilter & shifter, uint32_t colors
} }
} }
SDLImageIndexed::SDLImageIndexed(const std::shared_ptr<SDLImageShared> & image, EImageBlitMode mode) SDLImageIndexed::SDLImageIndexed(const std::shared_ptr<ISharedImage> & image, SDL_Palette * originalPalette, EImageBlitMode mode)
:SDLImageBase::SDLImageBase(image, mode) :SDLImageBase::SDLImageBase(image, mode)
,originalPalette(originalPalette)
{ {
auto originalPalette = image->getPalette();
currentPalette = SDL_AllocPalette(originalPalette->ncolors); currentPalette = SDL_AllocPalette(originalPalette->ncolors);
SDL_SetPaletteColors(currentPalette, originalPalette->colors, 0, originalPalette->ncolors); SDL_SetPaletteColors(currentPalette, originalPalette->colors, 0, originalPalette->ncolors);
setOverlayColor(Colors::TRANSPARENCY);
setShadowTransparency(0);
} }
SDLImageIndexed::~SDLImageIndexed() SDLImageIndexed::~SDLImageIndexed()
@@ -279,42 +331,96 @@ SDLImageIndexed::~SDLImageIndexed()
SDL_FreePalette(currentPalette); SDL_FreePalette(currentPalette);
} }
void SDLImageIndexed::setSpecialPalette(const IImage::SpecialPalette & specialPalette, uint32_t colorsToSkipMask) void SDLImageIndexed::setShadowTransparency(float factor)
{ {
size_t last = std::min<size_t>(specialPalette.size(), currentPalette->ncolors); ColorRGBA shadow50(0, 0, 0, 128 * factor);
ColorRGBA shadow25(0, 0, 0, 64 * factor);
for (size_t i = 0; i < last; ++i) // seems to be used unconditionally
currentPalette->colors[1] = CSDL_Ext::toSDL(shadow25);
currentPalette->colors[4] = CSDL_Ext::toSDL(shadow50);
// seems to be used only if color matches
if (colorsSimilar(originalPalette->colors[2], sourcePalette[2]))
currentPalette->colors[2] = CSDL_Ext::toSDL(shadow25);
if (colorsSimilar(originalPalette->colors[3], sourcePalette[3]))
currentPalette->colors[3] = CSDL_Ext::toSDL(shadow50);
}
void SDLImageIndexed::setOverlayColor(const ColorRGBA & color)
{
for (int i : {5,6,7})
{ {
if(i < std::numeric_limits<uint32_t>::digits && ((colorsToSkipMask >> i) & 1) == 1) if (colorsSimilar(originalPalette->colors[i], sourcePalette[i]))
currentPalette->colors[i] = CSDL_Ext::toSDL(specialPalette[i]); currentPalette->colors[i] = CSDL_Ext::toSDL(addColors(targetPalette[i], color));
} }
} }
void SDLImageIndexed::setShadowEnabled(bool on)
{
if (on)
setShadowTransparency(1.0);
else
setShadowTransparency(0);
shadowEnabled = on;
}
void SDLImageIndexed::setBodyEnabled(bool on)
{
if (on)
adjustPalette(ColorFilter::genEmptyShifter(), 0);
else
adjustPalette(ColorFilter::genAlphaShifter(0), 0);
bodyEnabled = on;
}
void SDLImageIndexed::setOverlayEnabled(bool on)
{
if (on)
setOverlayColor(Colors::WHITE_TRUE);
else
setOverlayColor(Colors::TRANSPARENCY);
overlayEnabled = on;
}
SDLImageShared::~SDLImageShared() SDLImageShared::~SDLImageShared()
{ {
SDL_FreeSurface(surf); SDL_FreeSurface(surf);
SDL_FreePalette(originalPalette); SDL_FreePalette(originalPalette);
} }
SDLImageBase::SDLImageBase(const std::shared_ptr<SDLImageShared> & image, EImageBlitMode mode) SDLImageBase::SDLImageBase(const std::shared_ptr<ISharedImage> & image, EImageBlitMode mode)
:image(image) :image(image)
, alphaValue(SDL_ALPHA_OPAQUE) , alphaValue(SDL_ALPHA_OPAQUE)
, blitMode(mode) , blitMode(mode)
{} {}
std::shared_ptr<ISharedImage> SDLImageBase::getSharedImage() const
{
return image;
}
void SDLImageRGB::draw(SDL_Surface * where, const Point & pos, const Rect * src) const void SDLImageRGB::draw(SDL_Surface * where, const Point & pos, const Rect * src) const
{ {
image->draw(where, nullptr, pos, src, alphaValue, blitMode); image->draw(where, nullptr, pos, src, Colors::WHITE_TRUE, alphaValue, blitMode);
} }
void SDLImageIndexed::draw(SDL_Surface * where, const Point & pos, const Rect * src) const void SDLImageIndexed::draw(SDL_Surface * where, const Point & pos, const Rect * src) const
{ {
image->draw(where, currentPalette, pos, src, alphaValue, blitMode); image->draw(where, currentPalette, pos, src, Colors::WHITE_TRUE, alphaValue, blitMode);
} }
void SDLImageBase::scaleFast(const Point & size) void SDLImageIndexed::scaleFast(const Point & size)
{ {
image = image->scaleFast(size); image = image->scaleFast(size, currentPalette);
}
void SDLImageRGB::scaleFast(const Point & size)
{
image = image->scaleFast(size, nullptr);
} }
void SDLImageBase::exportBitmap(const boost::filesystem::path & path) const void SDLImageBase::exportBitmap(const boost::filesystem::path & path) const
@@ -342,15 +448,27 @@ void SDLImageBase::setBlitMode(EImageBlitMode mode)
blitMode = mode; blitMode = mode;
} }
void SDLImageRGB::setSpecialPalette(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) void SDLImageRGB::setShadowEnabled(bool on)
{
// Not supported. Theoretically we can try to extract all pixels of specific colors, but better to use 8-bit images or composite images
}
void SDLImageRGB::setBodyEnabled(bool on)
{
// Not supported. Theoretically we can try to extract all pixels of specific colors, but better to use 8-bit images or composite images
}
void SDLImageRGB::setOverlayEnabled(bool on)
{
// Not supported. Theoretically we can try to extract all pixels of specific colors, but better to use 8-bit images or composite images
}
void SDLImageRGB::setOverlayColor(const ColorRGBA & color)
{} {}
void SDLImageRGB::playerColored(PlayerColor player) void SDLImageRGB::playerColored(PlayerColor player)
{} {}
void SDLImageRGB::setFlagColor(PlayerColor player)
{}
void SDLImageRGB::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) void SDLImageRGB::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove)
{} {}

View File

@@ -47,7 +47,7 @@ public:
SDLImageShared(SDL_Surface * from); SDLImageShared(SDL_Surface * from);
~SDLImageShared(); ~SDLImageShared();
void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, uint8_t alpha, EImageBlitMode mode) const override; void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const override;
void exportBitmap(const boost::filesystem::path & path) const override; void exportBitmap(const boost::filesystem::path & path) const override;
Point dimensions() const override; Point dimensions() const override;
@@ -55,9 +55,7 @@ public:
std::shared_ptr<IImage> createImageReference(EImageBlitMode mode) override; std::shared_ptr<IImage> createImageReference(EImageBlitMode mode) override;
std::shared_ptr<ISharedImage> horizontalFlip() const override; std::shared_ptr<ISharedImage> horizontalFlip() const override;
std::shared_ptr<ISharedImage> verticalFlip() const override; std::shared_ptr<ISharedImage> verticalFlip() const override;
std::shared_ptr<SDLImageShared> scaleFast(const Point & size) const; std::shared_ptr<ISharedImage> scaleFast(const Point & size, SDL_Palette * palette) const override;
const SDL_Palette * getPalette() const;
friend class SDLImageLoader; friend class SDLImageLoader;
}; };
@@ -65,36 +63,46 @@ public:
class SDLImageBase : public IImage, boost::noncopyable class SDLImageBase : public IImage, boost::noncopyable
{ {
protected: protected:
std::shared_ptr<SDLImageShared> image; std::shared_ptr<ISharedImage> image;
uint8_t alphaValue; uint8_t alphaValue;
EImageBlitMode blitMode; EImageBlitMode blitMode;
public: public:
SDLImageBase(const std::shared_ptr<SDLImageShared> & image, EImageBlitMode mode); SDLImageBase(const std::shared_ptr<ISharedImage> & image, EImageBlitMode mode);
void scaleFast(const Point & size) override;
void exportBitmap(const boost::filesystem::path & path) const override; void exportBitmap(const boost::filesystem::path & path) const override;
bool isTransparent(const Point & coords) const override; bool isTransparent(const Point & coords) const override;
Point dimensions() const override; Point dimensions() const override;
void setAlpha(uint8_t value) override; void setAlpha(uint8_t value) override;
void setBlitMode(EImageBlitMode mode) override; void setBlitMode(EImageBlitMode mode) override;
std::shared_ptr<ISharedImage> getSharedImage() const override;
}; };
class SDLImageIndexed final : public SDLImageBase class SDLImageIndexed final : public SDLImageBase
{ {
SDL_Palette * currentPalette = nullptr; SDL_Palette * currentPalette = nullptr;
SDL_Palette * originalPalette = nullptr;
bool bodyEnabled = true;
bool shadowEnabled = false;
bool overlayEnabled = false;
void setShadowTransparency(float factor);
public: public:
SDLImageIndexed(const std::shared_ptr<SDLImageShared> & image, EImageBlitMode mode); SDLImageIndexed(const std::shared_ptr<ISharedImage> & image, SDL_Palette * palette, EImageBlitMode mode);
~SDLImageIndexed(); ~SDLImageIndexed();
void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override; void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override;
void setSpecialPalette(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) override; void setOverlayColor(const ColorRGBA & color) override;
void playerColored(PlayerColor player) override; void playerColored(PlayerColor player) override;
void setFlagColor(PlayerColor player) override;
void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override;
void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override;
void scaleFast(const Point & size) override;
void setShadowEnabled(bool on) override;
void setBodyEnabled(bool on) override;
void setOverlayEnabled(bool on) override;
}; };
class SDLImageRGB final : public SDLImageBase class SDLImageRGB final : public SDLImageBase
@@ -103,9 +111,13 @@ public:
using SDLImageBase::SDLImageBase; using SDLImageBase::SDLImageBase;
void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override; void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override;
void setSpecialPalette(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) override; void setOverlayColor(const ColorRGBA & color) override;
void playerColored(PlayerColor player) override; void playerColored(PlayerColor player) override;
void setFlagColor(PlayerColor player) override;
void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override; void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override;
void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override; void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override;
void scaleFast(const Point & size) override;
void setShadowEnabled(bool on) override;
void setBodyEnabled(bool on) override;
void setOverlayEnabled(bool on) override;
}; };