mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +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:
parent
2d12cecded
commit
ffba847f40
@ -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));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
ColorRGBA shadowTest = shifter.shiftColor(genShadow(128));
|
||||
@ -366,11 +339,12 @@ void CreatureAnimation::nextFrame(Canvas & canvas, const ColorFilter & shifter,
|
||||
|
||||
if(image)
|
||||
{
|
||||
IImage::SpecialPalette SpecialPalette;
|
||||
genSpecialPalette(SpecialPalette);
|
||||
image->setShadowEnabled(true);
|
||||
image->setOverlayEnabled(isIdle());
|
||||
if (isIdle())
|
||||
image->setOverlayColor(genBorderColor(getBorderStrength(elapsedTime), border));
|
||||
|
||||
image->setSpecialPalette(SpecialPalette, IImage::SPECIAL_PALETTE_MASK_CREATURES);
|
||||
image->adjustPalette(shifter, IImage::SPECIAL_PALETTE_MASK_CREATURES);
|
||||
image->adjustPalette(shifter, 0);
|
||||
|
||||
canvas.draw(image, pos.topLeft(), Rect(0, 0, pos.w, pos.h));
|
||||
|
||||
|
@ -107,9 +107,7 @@ private:
|
||||
|
||||
void endAnimation();
|
||||
|
||||
void genSpecialPalette(IImage::SpecialPalette & target);
|
||||
public:
|
||||
|
||||
/// function(s) that will be called when animation ends, after reset to 1st frame
|
||||
/// NOTE that these functions will be fired only once
|
||||
CFunctionList<void()> onAnimationReset;
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "../render/IImage.h"
|
||||
#include "../render/IRenderHandler.h"
|
||||
#include "../render/Colors.h"
|
||||
#include "../render/Graphics.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
|
||||
@ -477,23 +478,20 @@ void MapRendererObjects::renderImage(IMapRendererContext & context, Canvas & tar
|
||||
return;
|
||||
|
||||
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);
|
||||
|
||||
if ( offsetPixels.x < image->dimensions().x && offsetPixels.y < image->dimensions().y)
|
||||
{
|
||||
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)));
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ class CAnimation;
|
||||
class IImage;
|
||||
class Canvas;
|
||||
class IMapRendererContext;
|
||||
enum class EImageBlitMode;
|
||||
enum class EImageBlitMode : uint8_t;
|
||||
|
||||
class MapTileStorage
|
||||
{
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../render/IImage.h"
|
||||
#include "../render/IRenderHandler.h"
|
||||
#include "../render/IScreenHandler.h"
|
||||
|
||||
#include "../../lib/filesystem/Filesystem.h"
|
||||
#include "../../lib/json/JsonUtils.h"
|
||||
|
@ -36,48 +36,10 @@ enum class DefType : uint32_t
|
||||
* 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):
|
||||
data(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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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++)
|
||||
{
|
||||
size_t blockID = read_le_u32(data.get() + it);
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
const ColorRGBA Colors::YELLOW = { 229, 215, 123, 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::GREEN = { 0, 255, 0, ColorRGBA::ALPHA_OPAQUE };
|
||||
const ColorRGBA Colors::CYAN = { 0, 255, 255, ColorRGBA::ALPHA_OPAQUE };
|
||||
|
@ -23,6 +23,9 @@ public:
|
||||
/** the standard h3 white color */
|
||||
static const ColorRGBA WHITE;
|
||||
|
||||
/** actual 100% white color */
|
||||
static const ColorRGBA WHITE_TRUE;
|
||||
|
||||
/** the metallic gold color used mostly as a border around buttons */
|
||||
static const ColorRGBA METALLIC_GOLD;
|
||||
|
||||
|
@ -23,9 +23,10 @@ VCMI_LIB_NAMESPACE_END
|
||||
struct SDL_Surface;
|
||||
struct SDL_Palette;
|
||||
class ColorFilter;
|
||||
class ISharedImage;
|
||||
|
||||
/// 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
|
||||
/// Indexed or RGBA: Image can have no transparency and can be only used as background
|
||||
@ -41,15 +42,11 @@ enum class EImageBlitMode
|
||||
ALPHA
|
||||
};
|
||||
|
||||
/*
|
||||
* Base class for images, can be used for non-animation pictures as well
|
||||
*/
|
||||
/// Base class for images for use in client code.
|
||||
/// This class represents current state of image, with potential transformations applied, such as player coloring
|
||||
class IImage
|
||||
{
|
||||
public:
|
||||
using SpecialPalette = std::vector<ColorRGBA>;
|
||||
static constexpr int32_t SPECIAL_PALETTE_MASK_CREATURES = 0b11110011;
|
||||
|
||||
//draws image on surface "where" at position
|
||||
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
|
||||
virtual void playerColored(PlayerColor player) = 0;
|
||||
|
||||
//set special color for flag
|
||||
virtual void setFlagColor(PlayerColor player) = 0;
|
||||
|
||||
//test transparency of specific pixel
|
||||
virtual bool isTransparent(const Point & coords) const = 0;
|
||||
|
||||
@ -78,23 +72,32 @@ public:
|
||||
virtual void setBlitMode(EImageBlitMode mode) = 0;
|
||||
|
||||
//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;
|
||||
};
|
||||
|
||||
/// 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
|
||||
{
|
||||
public:
|
||||
virtual Point dimensions() const = 0;
|
||||
virtual void exportBitmap(const boost::filesystem::path & path) 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<ISharedImage> horizontalFlip() 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;
|
||||
|
@ -19,7 +19,7 @@ struct SDL_Surface;
|
||||
|
||||
class IImage;
|
||||
class CAnimation;
|
||||
enum class EImageBlitMode;
|
||||
enum class EImageBlitMode : uint8_t;
|
||||
|
||||
class IRenderHandler : public boost::noncopyable
|
||||
{
|
||||
|
@ -10,6 +10,9 @@
|
||||
#include "StdInc.h"
|
||||
#include "ImageLocator.h"
|
||||
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "IScreenHandler.h"
|
||||
|
||||
#include "../../lib/json/JsonNode.h"
|
||||
|
||||
|
||||
@ -47,10 +50,46 @@ bool ImageLocator::operator<(const ImageLocator & other) const
|
||||
return defFrame < other.defFrame;
|
||||
if(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
|
||||
{
|
||||
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
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../lib/filesystem/ResourcePath.h"
|
||||
#include "../../lib/constants/EntityIdentifiers.h"
|
||||
|
||||
struct ImageLocator
|
||||
{
|
||||
@ -20,6 +21,11 @@ struct ImageLocator
|
||||
|
||||
bool verticalFlip = 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(const AnimationPath & path, int frame, int group);
|
||||
@ -28,4 +34,8 @@ struct ImageLocator
|
||||
|
||||
bool operator < (const ImageLocator & other) const;
|
||||
bool empty() const;
|
||||
|
||||
ImageLocator copyFile() const;
|
||||
ImageLocator copyFileTransform() const;
|
||||
ImageLocator copyFileTransformScale() const;
|
||||
};
|
||||
|
@ -13,88 +13,49 @@
|
||||
#include "SDLImage.h"
|
||||
#include "SDL_Extensions.h"
|
||||
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../render/IScreenHandler.h"
|
||||
#include "../render/Colors.h"
|
||||
|
||||
#include "../../lib/constants/EntityIdentifiers.h"
|
||||
|
||||
#include <SDL_surface.h>
|
||||
|
||||
ImageSharedScaled::ImageSharedScaled(std::shared_ptr<SDLImageShared> sourceImage)
|
||||
:sourceImage(sourceImage)
|
||||
{
|
||||
scaledImage = sourceImage->scaleFast(sourceImage->dimensions() * getScalingFactor());
|
||||
}
|
||||
|
||||
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)
|
||||
ImageScaled::ImageScaled(const ImageLocator & inputLocator, const std::shared_ptr<ISharedImage> & source, EImageBlitMode mode)
|
||||
: source(source)
|
||||
, locator(inputLocator)
|
||||
, colorMultiplier(Colors::WHITE_TRUE)
|
||||
, alphaValue(SDL_ALPHA_OPAQUE)
|
||||
, blitMode(mode)
|
||||
{
|
||||
locator.scalingFactor = GH.screenHandler().getScalingFactor();
|
||||
setBodyEnabled(true);
|
||||
}
|
||||
|
||||
std::shared_ptr<ISharedImage> ImageScaled::getSharedImage() const
|
||||
{
|
||||
return body;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
image->exportBitmap(path);
|
||||
source->exportBitmap(path);
|
||||
}
|
||||
|
||||
bool ImageScaled::isTransparent(const Point &coords) const
|
||||
{
|
||||
return image->isTransparent(coords);
|
||||
return source->isTransparent(coords);
|
||||
}
|
||||
|
||||
Point ImageScaled::dimensions() const
|
||||
{
|
||||
return image->dimensions();
|
||||
return source->dimensions();
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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::setFlagColor(PlayerColor player)
|
||||
{
|
||||
playerColor = player;
|
||||
if (body)
|
||||
setBodyEnabled(true); // regenerate
|
||||
}
|
||||
|
||||
void ImageScaled::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove)
|
||||
{
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void ImageScaled::adjustPalette(const ColorFilter &shifter, uint32_t colorsToSkipMask)
|
||||
{
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void ImageScaled::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;
|
||||
}
|
||||
|
@ -12,40 +12,40 @@
|
||||
#include "../render/IImage.h"
|
||||
#include "../render/IRenderHandler.h"
|
||||
|
||||
#include "../../lib/Color.h"
|
||||
#include "../../lib/constants/EntityIdentifiers.h"
|
||||
|
||||
struct SDL_Palette;
|
||||
|
||||
class SDLImageShared;
|
||||
|
||||
class ImageSharedScaled final : public ISharedImage, public std::enable_shared_from_this<ImageSharedScaled>, boost::noncopyable
|
||||
{
|
||||
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;
|
||||
};
|
||||
|
||||
// Upscaled image with several mechanisms to emulate H3 palette effects
|
||||
class ImageScaled final : public IImage
|
||||
{
|
||||
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;
|
||||
EImageBlitMode blitMode;
|
||||
|
||||
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 exportBitmap(const boost::filesystem::path & path) const override;
|
||||
@ -54,9 +54,13 @@ public:
|
||||
void setAlpha(uint8_t value) override;
|
||||
void setBlitMode(EImageBlitMode mode) override;
|
||||
void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override;
|
||||
void setSpecialPalette(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) override;
|
||||
void setOverlayColor(const ColorRGBA & color) override;
|
||||
void playerColored(PlayerColor player) override;
|
||||
void setFlagColor(PlayerColor player) override;
|
||||
void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override;
|
||||
void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override;
|
||||
|
||||
void setShadowEnabled(bool on) override;
|
||||
void setBodyEnabled(bool on) override;
|
||||
void setOverlayEnabled(bool on) override;
|
||||
std::shared_ptr<ISharedImage> getSharedImage() const override;
|
||||
};
|
||||
|
@ -15,6 +15,8 @@
|
||||
|
||||
#include "../render/CAnimation.h"
|
||||
#include "../render/CDefFile.h"
|
||||
#include "../render/Colors.h"
|
||||
#include "../render/ColorFilter.h"
|
||||
|
||||
#include "../../lib/json/JsonUtils.h"
|
||||
#include "../../lib/filesystem/Filesystem.h"
|
||||
@ -131,47 +133,28 @@ int RenderHandler::getScalingFactor() const
|
||||
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)
|
||||
return input;
|
||||
|
||||
return std::make_shared<ImageSharedScaled>(input);
|
||||
if (getScalingFactor() == 1 || locator.scalingFactor != 1 || locator.empty())
|
||||
return input->createImageReference(mode);
|
||||
else
|
||||
return std::make_shared<ImageScaled>(locator, input, mode);
|
||||
}
|
||||
|
||||
std::shared_ptr<ISharedImage> RenderHandler::loadImageFromSingleFile(const ImagePath & path)
|
||||
{
|
||||
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)
|
||||
ImageLocator RenderHandler::getLocatorForAnimationFrame(const AnimationPath & path, int frame, int group)
|
||||
{
|
||||
const auto & layout = getAnimationLayout(path);
|
||||
if (!layout.count(group))
|
||||
return loadImageFromSingleFile(ImagePath::builtin("DEFAULT"));
|
||||
return ImageLocator(ImagePath::builtin("DEFAULT"));
|
||||
|
||||
if (frame >= layout.at(group).size())
|
||||
return loadImageFromSingleFile(ImagePath::builtin("DEFAULT"));
|
||||
return ImageLocator(ImagePath::builtin("DEFAULT"));
|
||||
|
||||
const auto & locator = layout.at(group).at(frame);
|
||||
if (locator.image)
|
||||
{
|
||||
return loadImageImpl(locator);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto defFile = getAnimationFile(path);
|
||||
return createScaledImage(std::make_shared<SDLImageShared>(defFile.get(), frame, group));
|
||||
}
|
||||
}
|
||||
if (locator.image || locator.defFile)
|
||||
return locator;
|
||||
|
||||
std::shared_ptr<ISharedImage> RenderHandler::loadImageFromAnimationFile(const AnimationPath & path, int frame, int group)
|
||||
{
|
||||
auto result = loadImageFromAnimationFileUncached(path, frame, group);
|
||||
imageFiles[ImageLocator(path, frame, group)] = result;
|
||||
return result;
|
||||
return ImageLocator(path, frame, group);
|
||||
}
|
||||
|
||||
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())
|
||||
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)
|
||||
result = loadImageFromSingleFile(*locator.image);
|
||||
else if (locator.defFile)
|
||||
result = loadImageFromAnimationFile(*locator.defFile, locator.defFrame, locator.defGroup);
|
||||
{
|
||||
// TODO: create EmptySharedImage class that will be instantiated if image does not exists or fails to load
|
||||
return std::make_shared<SDLImageShared>(*locator.image);
|
||||
}
|
||||
|
||||
if (!result)
|
||||
result = loadImageFromSingleFile(ImagePath::builtin("DEFAULT"));
|
||||
if (locator.defFile)
|
||||
{
|
||||
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)
|
||||
result = result->verticalFlip();
|
||||
@ -200,24 +220,50 @@ std::shared_ptr<ISharedImage> RenderHandler::loadImageImpl(const ImageLocator &
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return loadImageImpl(ImageLocator(path))->createImageReference(mode);
|
||||
return loadImage(ImageLocator(path), mode);
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -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 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> 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;
|
||||
|
||||
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:
|
||||
|
||||
// IRenderHandler implementation
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "SDL_Extensions.h"
|
||||
|
||||
#include "../render/ColorFilter.h"
|
||||
#include "../render/Colors.h"
|
||||
#include "../render/CBitmapHandler.h"
|
||||
#include "../render/CDefFile.h"
|
||||
#include "../render/Graphics.h"
|
||||
@ -22,6 +23,59 @@
|
||||
|
||||
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
|
||||
{
|
||||
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)
|
||||
return;
|
||||
@ -109,6 +163,7 @@ void SDLImageShared::draw(SDL_Surface * where, SDL_Palette * palette, const Poin
|
||||
|
||||
destShift += dest;
|
||||
|
||||
SDL_SetSurfaceColorMod(surf, colorMultiplier.r, colorMultiplier.g, colorMultiplier.b);
|
||||
SDL_SetSurfaceAlphaMod(surf, alpha);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (surf->format->palette)
|
||||
SDL_SetSurfacePalette(surf, originalPalette);
|
||||
}
|
||||
|
||||
const SDL_Palette * SDLImageShared::getPalette() const
|
||||
{
|
||||
if (originalPalette == nullptr)
|
||||
throw std::runtime_error("Palette not found!");
|
||||
|
||||
return originalPalette;
|
||||
}
|
||||
|
||||
std::shared_ptr<SDLImageShared> SDLImageShared::scaleFast(const Point & size) const
|
||||
std::shared_ptr<ISharedImage> SDLImageShared::scaleFast(const Point & size, SDL_Palette * palette) const
|
||||
{
|
||||
float scaleX = float(size.x) / dimensions().x;
|
||||
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));
|
||||
|
||||
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
|
||||
SDL_FreeSurface(scaled);
|
||||
|
||||
if (surf->format->palette)
|
||||
SDL_SetSurfacePalette(surf, originalPalette);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -175,12 +231,6 @@ void SDLImageIndexed::playerColored(PlayerColor 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
|
||||
{
|
||||
if (surf)
|
||||
@ -197,7 +247,7 @@ Point SDLImageShared::dimensions() const
|
||||
std::shared_ptr<IImage> SDLImageShared::createImageReference(EImageBlitMode mode)
|
||||
{
|
||||
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
|
||||
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)
|
||||
{
|
||||
const SDL_Palette * originalPalette = image->getPalette();
|
||||
|
||||
std::vector<SDL_Color> shifterColors(colorsToMove);
|
||||
|
||||
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)
|
||||
{
|
||||
const SDL_Palette * originalPalette = image->getPalette();
|
||||
|
||||
// Note: here we skip first colors in the palette that are predefined in H3 images
|
||||
for(int i = 0; i < currentPalette->ncolors; i++)
|
||||
{
|
||||
if (i < std::size(sourcePalette) && colorsSimilar(sourcePalette[i], originalPalette->colors[i]))
|
||||
continue;
|
||||
|
||||
if(i < std::numeric_limits<uint32_t>::digits && ((colorsToSkipMask >> i) & 1) == 1)
|
||||
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)
|
||||
,originalPalette(originalPalette)
|
||||
{
|
||||
auto originalPalette = image->getPalette();
|
||||
|
||||
currentPalette = SDL_AllocPalette(originalPalette->ncolors);
|
||||
SDL_SetPaletteColors(currentPalette, originalPalette->colors, 0, originalPalette->ncolors);
|
||||
|
||||
setOverlayColor(Colors::TRANSPARENCY);
|
||||
setShadowTransparency(0);
|
||||
}
|
||||
|
||||
SDLImageIndexed::~SDLImageIndexed()
|
||||
@ -279,42 +331,96 @@ SDLImageIndexed::~SDLImageIndexed()
|
||||
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)
|
||||
currentPalette->colors[i] = CSDL_Ext::toSDL(specialPalette[i]);
|
||||
if (colorsSimilar(originalPalette->colors[i], sourcePalette[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()
|
||||
{
|
||||
SDL_FreeSurface(surf);
|
||||
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)
|
||||
, alphaValue(SDL_ALPHA_OPAQUE)
|
||||
, blitMode(mode)
|
||||
{}
|
||||
|
||||
std::shared_ptr<ISharedImage> SDLImageBase::getSharedImage() const
|
||||
{
|
||||
return image;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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
|
||||
@ -342,15 +448,27 @@ void SDLImageBase::setBlitMode(EImageBlitMode 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::setFlagColor(PlayerColor player)
|
||||
{}
|
||||
|
||||
void SDLImageRGB::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove)
|
||||
{}
|
||||
|
||||
|
@ -47,7 +47,7 @@ public:
|
||||
SDLImageShared(SDL_Surface * from);
|
||||
~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;
|
||||
Point dimensions() const override;
|
||||
@ -55,9 +55,7 @@ public:
|
||||
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<SDLImageShared> scaleFast(const Point & size) const;
|
||||
|
||||
const SDL_Palette * getPalette() const;
|
||||
std::shared_ptr<ISharedImage> scaleFast(const Point & size, SDL_Palette * palette) const override;
|
||||
|
||||
friend class SDLImageLoader;
|
||||
};
|
||||
@ -65,36 +63,46 @@ public:
|
||||
class SDLImageBase : public IImage, boost::noncopyable
|
||||
{
|
||||
protected:
|
||||
std::shared_ptr<SDLImageShared> image;
|
||||
std::shared_ptr<ISharedImage> image;
|
||||
|
||||
uint8_t alphaValue;
|
||||
EImageBlitMode blitMode;
|
||||
|
||||
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;
|
||||
bool isTransparent(const Point & coords) const override;
|
||||
Point dimensions() const override;
|
||||
void setAlpha(uint8_t value) override;
|
||||
void setBlitMode(EImageBlitMode mode) override;
|
||||
std::shared_ptr<ISharedImage> getSharedImage() const override;
|
||||
};
|
||||
|
||||
class SDLImageIndexed final : public SDLImageBase
|
||||
{
|
||||
SDL_Palette * currentPalette = nullptr;
|
||||
SDL_Palette * originalPalette = nullptr;
|
||||
|
||||
bool bodyEnabled = true;
|
||||
bool shadowEnabled = false;
|
||||
bool overlayEnabled = false;
|
||||
|
||||
void setShadowTransparency(float factor);
|
||||
public:
|
||||
SDLImageIndexed(const std::shared_ptr<SDLImageShared> & image, EImageBlitMode mode);
|
||||
SDLImageIndexed(const std::shared_ptr<ISharedImage> & image, SDL_Palette * palette, EImageBlitMode mode);
|
||||
~SDLImageIndexed();
|
||||
|
||||
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 setFlagColor(PlayerColor player) override;
|
||||
void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override;
|
||||
void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override;
|
||||
void 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
|
||||
@ -103,9 +111,13 @@ public:
|
||||
using SDLImageBase::SDLImageBase;
|
||||
|
||||
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 setFlagColor(PlayerColor player) override;
|
||||
void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override;
|
||||
void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override;
|
||||
void scaleFast(const Point & size) override;
|
||||
|
||||
void setShadowEnabled(bool on) override;
|
||||
void setBodyEnabled(bool on) override;
|
||||
void setOverlayEnabled(bool on) override;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user