mirror of https://github.com/OpenTTD/OpenTTD
Add: Functions to convert colours between RGB and HSV space.
parent
504a26a60a
commit
fb7c5e04cf
|
@ -303,6 +303,7 @@ enum Colours : uint32_t {
|
||||||
};
|
};
|
||||||
DECLARE_INCREMENT_DECREMENT_OPERATORS(Colours)
|
DECLARE_INCREMENT_DECREMENT_OPERATORS(Colours)
|
||||||
DECLARE_ENUM_AS_ADDABLE(Colours)
|
DECLARE_ENUM_AS_ADDABLE(Colours)
|
||||||
|
DECLARE_ENUM_AS_BIT_SET(Colours)
|
||||||
|
|
||||||
/** Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palette.png */
|
/** Colour of the strings, see _string_colourmap in table/string_colours.h or docs/ottd-colourtext-palette.png */
|
||||||
enum TextColour : uint32_t {
|
enum TextColour : uint32_t {
|
||||||
|
@ -420,4 +421,15 @@ struct PixelColour {
|
||||||
TextColour ToTextColour() const;
|
TextColour ToTextColour() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct HsvColour {
|
||||||
|
static constexpr int HUE_MAX = 360 * 128; ///< Maximum value for hue.
|
||||||
|
static constexpr int SAT_MAX = UINT8_MAX; ///< Maximum value for saturation.
|
||||||
|
static constexpr int VAL_MAX = UINT8_MAX; ///< Maximum value for value.
|
||||||
|
static constexpr int HUE_RGN = HUE_MAX / 6;
|
||||||
|
|
||||||
|
uint16_t h; ///< Hue ranging from 0 to HUE_MAX.
|
||||||
|
uint8_t s; ///< Saturation ranging from 0 to SAT_MAX.
|
||||||
|
uint8_t v; ///< Value (brightness) ranging from 0 to VAL_MAX.
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* GFX_TYPE_H */
|
#endif /* GFX_TYPE_H */
|
||||||
|
|
|
@ -368,6 +368,81 @@ TextColour GetContrastColour(PixelColour background, uint8_t threshold)
|
||||||
return sq1000_brightness < ((uint) threshold) * ((uint) threshold) * 1000 ? TC_WHITE : TC_BLACK;
|
return sq1000_brightness < ((uint) threshold) * ((uint) threshold) * 1000 ? TC_WHITE : TC_BLACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HsvColour ConvertRgbToHsv(Colour rgb)
|
||||||
|
{
|
||||||
|
HsvColour hsv;
|
||||||
|
|
||||||
|
uint8_t rgbmin = std::min({rgb.r, rgb.g, rgb.b});
|
||||||
|
uint8_t rgbmax = std::max({rgb.r, rgb.g, rgb.b});
|
||||||
|
|
||||||
|
hsv.v = rgbmax;
|
||||||
|
if (hsv.v == 0) {
|
||||||
|
hsv.h = 0;
|
||||||
|
hsv.s = 0;
|
||||||
|
return hsv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int d = rgbmax - rgbmin;
|
||||||
|
hsv.s = HsvColour::SAT_MAX * d / rgbmax;
|
||||||
|
if (hsv.s == 0) {
|
||||||
|
hsv.h = 0;
|
||||||
|
return hsv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hue;
|
||||||
|
if (rgbmax == rgb.r) {
|
||||||
|
hue = HsvColour::HUE_RGN * 0 + HsvColour::HUE_RGN * ((int)rgb.g - (int)rgb.b) / d;
|
||||||
|
} else if (rgbmax == rgb.g) {
|
||||||
|
hue = HsvColour::HUE_RGN * 2 + HsvColour::HUE_RGN * ((int)rgb.b - (int)rgb.r) / d;
|
||||||
|
} else {
|
||||||
|
hue = HsvColour::HUE_RGN * 4 + HsvColour::HUE_RGN * ((int)rgb.r - (int)rgb.g) / d;
|
||||||
|
}
|
||||||
|
if (hue > HsvColour::HUE_MAX) hue -= HsvColour::HUE_MAX;
|
||||||
|
if (hue < 0) hue += HsvColour::HUE_MAX;
|
||||||
|
hsv.h = hue;
|
||||||
|
|
||||||
|
return hsv;
|
||||||
|
}
|
||||||
|
|
||||||
|
Colour ConvertHsvToRgb(HsvColour hsv)
|
||||||
|
{
|
||||||
|
if (hsv.s == 0) return Colour(hsv.v, hsv.v, hsv.v);
|
||||||
|
if (hsv.h >= HsvColour::HUE_MAX) hsv.h = 0;
|
||||||
|
|
||||||
|
int region = hsv.h / HsvColour::HUE_RGN;
|
||||||
|
int remainder = (hsv.h - (region * HsvColour::HUE_RGN)) * 6;
|
||||||
|
int p = (hsv.v * (HsvColour::SAT_MAX - hsv.s)) / HsvColour::SAT_MAX;
|
||||||
|
int q = (hsv.v * (HsvColour::SAT_MAX - ((hsv.s * remainder) / HsvColour::HUE_MAX))) / HsvColour::SAT_MAX;
|
||||||
|
int t = (hsv.v * (HsvColour::SAT_MAX - ((hsv.s * (HsvColour::HUE_MAX - remainder)) / HsvColour::HUE_MAX))) / HsvColour::SAT_MAX;
|
||||||
|
|
||||||
|
switch (region) {
|
||||||
|
case 0: return Colour(hsv.v, t, p);
|
||||||
|
case 1: return Colour(q, hsv.v, p);
|
||||||
|
case 2: return Colour(p, hsv.v, t);
|
||||||
|
case 3: return Colour(p, q, hsv.v);
|
||||||
|
case 4: return Colour(t, p, hsv.v);
|
||||||
|
default: return Colour(hsv.v, p, q);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjust brightness of an HSV colour.
|
||||||
|
* @param hsv colour to adjust.
|
||||||
|
* @param shade shade to apply.
|
||||||
|
* @param contrast contrast of shade.
|
||||||
|
* @returns Adjusted HSV colour.
|
||||||
|
**/
|
||||||
|
HsvColour AdjustHsvColourBrightness(HsvColour hsv, ColourShade shade, int contrast)
|
||||||
|
{
|
||||||
|
HsvColour r = hsv;
|
||||||
|
int amt = (shade - SHADE_NORMAL) * (16 + contrast) / 8;
|
||||||
|
int overflow = (hsv.v + amt) - HsvColour::VAL_MAX;
|
||||||
|
r.v = ClampTo<uint8_t>(hsv.v + amt);
|
||||||
|
r.s = ClampTo<uint8_t>(hsv.s - std::max(0, overflow));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lookup table of colour shades for all 16 colour gradients.
|
* Lookup table of colour shades for all 16 colour gradients.
|
||||||
* 8 colours per gradient from darkest (0) to lightest (7)
|
* 8 colours per gradient from darkest (0) to lightest (7)
|
||||||
|
@ -387,6 +462,10 @@ struct ColourGradients
|
||||||
*/
|
*/
|
||||||
PixelColour GetColourGradient(Colours colour, ColourShade shade)
|
PixelColour GetColourGradient(Colours colour, ColourShade shade)
|
||||||
{
|
{
|
||||||
|
ColoursPacker cp(colour);
|
||||||
|
if (cp.IsCustom()) {
|
||||||
|
return ConvertHsvToRgb(AdjustHsvColourBrightness(cp.Hsv(), shade, cp.GetContrast()));
|
||||||
|
}
|
||||||
return ColourGradients::gradient[colour % COLOUR_END][shade % SHADE_END];
|
return ColourGradients::gradient[colour % COLOUR_END][shade % SHADE_END];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,10 @@ enum ColourShade : uint8_t {
|
||||||
};
|
};
|
||||||
DECLARE_INCREMENT_DECREMENT_OPERATORS(ColourShade)
|
DECLARE_INCREMENT_DECREMENT_OPERATORS(ColourShade)
|
||||||
|
|
||||||
|
HsvColour ConvertRgbToHsv(Colour rgb);
|
||||||
|
Colour ConvertHsvToRgb(HsvColour hsv);
|
||||||
|
HsvColour AdjustHsvColourBrightness(HsvColour hsv, ColourShade shade, int contrast);
|
||||||
|
|
||||||
PixelColour GetColourGradient(Colours colour, ColourShade shade);
|
PixelColour GetColourGradient(Colours colour, ColourShade shade);
|
||||||
void SetColourGradient(Colours colour, ColourShade shade, PixelColour palette_colour);
|
void SetColourGradient(Colours colour, ColourShade shade, PixelColour palette_colour);
|
||||||
|
|
||||||
|
@ -137,6 +141,47 @@ inline constexpr uint8_t StretchBits(uint8_t v)
|
||||||
return (v << (8 - TNumBits)) | (v >> (8 - (8 - TNumBits) * 2));
|
return (v << (8 - TNumBits)) | (v >> (8 - (8 - TNumBits) * 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ColoursPacker
|
||||||
|
{
|
||||||
|
Colours &c;
|
||||||
|
|
||||||
|
explicit constexpr ColoursPacker(Colours &c) : c(c) { }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constants for the bit packing used by Colours.
|
||||||
|
*/
|
||||||
|
static constexpr const uint IS_CUSTOM_BIT = 4;
|
||||||
|
static constexpr const uint INDEX_START = 0; ///< Packed start of index component
|
||||||
|
static constexpr const uint INDEX_SIZE = 4; ///< Packed size of index component
|
||||||
|
static constexpr const uint HUE_START = 7; ///< Packed start of hue component
|
||||||
|
static constexpr const uint HUE_SIZE = 9; ///< Packed size of hue component
|
||||||
|
static constexpr const uint SAT_START = 16; ///< Packed start of saturation component
|
||||||
|
static constexpr const uint SAT_SIZE = 6; ///< Packed size of saturation component
|
||||||
|
static constexpr const uint VAL_START = 22; ///< Packed start of value component
|
||||||
|
static constexpr const uint VAL_SIZE = 6; ///< Packed size of value component
|
||||||
|
static constexpr const uint CON_START = 28; ///< Packed start of contrast component
|
||||||
|
static constexpr const uint CON_SIZE = 4; ///< Packed size of contrast component
|
||||||
|
|
||||||
|
/* Colours is considered unused and blank if only the I component is set. */
|
||||||
|
inline constexpr bool IsCustom() const { return HasBit(this->c, IS_CUSTOM_BIT); }
|
||||||
|
|
||||||
|
inline constexpr uint8_t GetIndex() const { return GB(this->c, INDEX_START, INDEX_SIZE); }
|
||||||
|
inline constexpr uint16_t GetHue() const { return GB(this->c, HUE_START, HUE_SIZE) * HsvColour::HUE_MAX / (1U << 9); }
|
||||||
|
inline constexpr uint8_t GetSaturation() const { return StretchBits<SAT_SIZE>(GB(this->c, SAT_START, SAT_SIZE)); }
|
||||||
|
inline constexpr uint8_t GetValue() const { return StretchBits<VAL_SIZE>(GB(this->c, VAL_START, VAL_SIZE)); }
|
||||||
|
inline constexpr uint8_t GetContrast() const { return StretchBits<CON_SIZE>(GB(this->c, CON_START, CON_SIZE)); }
|
||||||
|
|
||||||
|
inline void SetCustom(bool v) { SB(this->c, IS_CUSTOM_BIT, 1, v); }
|
||||||
|
|
||||||
|
inline void SetIndex(uint8_t v) { SB(this->c, INDEX_START, INDEX_SIZE, v); }
|
||||||
|
inline void SetHue(uint16_t v) { SB(this->c, HUE_START, HUE_SIZE, v * (1U << HUE_SIZE) / HsvColour::HUE_MAX); }
|
||||||
|
inline void SetSaturation(uint8_t v) { SB(this->c, SAT_START, SAT_SIZE, v >> (8 - SAT_SIZE)); }
|
||||||
|
inline void SetValue(uint8_t v) { SB(this->c, VAL_START, VAL_SIZE, v >> (8 - VAL_SIZE)); }
|
||||||
|
inline void SetContrast(uint8_t v) { SB(this->c, CON_START, CON_SIZE, v >> (8 - CON_SIZE)); }
|
||||||
|
|
||||||
|
inline constexpr HsvColour Hsv() const { return {this->GetHue(), this->GetSaturation(), this->GetValue()}; }
|
||||||
|
};
|
||||||
|
|
||||||
struct TextColourPacker
|
struct TextColourPacker
|
||||||
{
|
{
|
||||||
TextColour &tc;
|
TextColour &tc;
|
||||||
|
|
Loading…
Reference in New Issue