diff options
| author | Maxime Coste <mawww@kakoune.org> | 2020-05-02 12:57:36 +1000 |
|---|---|---|
| committer | Maxime Coste <mawww@kakoune.org> | 2020-05-02 12:57:36 +1000 |
| commit | ccecd5bd8e871b7aa89568609fd4ba7c50fd52ad (patch) | |
| tree | 49978f00acc7893c2d746b3111fa8fe5083699d1 /src | |
| parent | c6906be475832558f057c4a22d00d56f128414bc (diff) | |
Add support for alpha channel in colors
This makes it easier to define faces that lighten/darken whatever
they apply on.
Diffstat (limited to 'src')
| -rw-r--r-- | src/color.cc | 21 | ||||
| -rw-r--r-- | src/color.hh | 25 | ||||
| -rw-r--r-- | src/commands.cc | 2 | ||||
| -rw-r--r-- | src/face.hh | 10 | ||||
| -rw-r--r-- | src/ncurses_ui.cc | 4 | ||||
| -rw-r--r-- | src/remote.cc | 4 |
6 files changed, 52 insertions, 14 deletions
diff --git a/src/color.cc b/src/color.cc index 4373684d..b355b9cf 100644 --- a/src/color.cc +++ b/src/color.cc @@ -34,6 +34,13 @@ bool is_color_name(StringView color) return contains(color_names, color); } +void Color::validate_alpha() +{ + static_assert(RGB == 17); + if (a < RGB) + throw runtime_error("Colors alpha must be > 16"); +} + Color str_to_color(StringView color) { auto it = find_if(color_names, [&](const char* c){ return color == c; }); @@ -55,6 +62,11 @@ Color str_to_color(StringView color) return { (unsigned char)(hval(color[4]) * 16 + hval(color[5])), (unsigned char)(hval(color[6]) * 16 + hval(color[7])), (unsigned char)(hval(color[8]) * 16 + hval(color[9])) }; + if (color.length() == 13 and color.substr(0_byte, 5_byte) == "rgba:") + return { (unsigned char)(hval(color[5]) * 16 + hval(color[6])), + (unsigned char)(hval(color[7]) * 16 + hval(color[8])), + (unsigned char)(hval(color[9]) * 16 + hval(color[10])), + (unsigned char)(hval(color[11]) * 16 + hval(color[12])) }; throw runtime_error(format("unable to parse color: '{}'", color)); return Color::Default; @@ -62,10 +74,13 @@ Color str_to_color(StringView color) String to_string(Color color) { - if (color.color == Color::RGB) + if (color.isRGB()) { - char buffer[11]; - sprintf(buffer, "rgb:%02x%02x%02x", color.r, color.g, color.b); + char buffer[14]; + if (color.a == 255) + sprintf(buffer, "rgb:%02x%02x%02x", color.r, color.g, color.b); + else + sprintf(buffer, "rgba:%02x%02x%02x%02x", color.r, color.g, color.b, color.a); return buffer; } else diff --git a/src/color.hh b/src/color.hh index d42b112b..943678ed 100644 --- a/src/color.hh +++ b/src/color.hh @@ -3,6 +3,7 @@ #include "hash.hh" #include "meta.hh" +#include "assert.hh" namespace Kakoune { @@ -12,7 +13,7 @@ class StringView; struct Color { - enum NamedColor : char + enum NamedColor : unsigned char { Default, Black, @@ -34,15 +35,27 @@ struct Color RGB, }; - NamedColor color; + union + { + NamedColor color; + unsigned char a; + }; unsigned char r = 0; unsigned char g = 0; unsigned char b = 0; + constexpr bool isRGB() const { return a >= RGB; } + constexpr Color() : Color{Default} {} constexpr Color(NamedColor c) : color{c} {} - constexpr Color(unsigned char r, unsigned char g, unsigned char b) - : color{RGB}, r{r}, g{g}, b{b} {} + constexpr Color(unsigned char r, unsigned char g, unsigned char b, unsigned char a = 255) + : a{a}, r{r}, g{g}, b{b} + { + validate_alpha(); + } + +private: + void validate_alpha(); }; constexpr bool operator==(Color lhs, Color rhs) @@ -66,8 +79,8 @@ bool is_color_name(StringView color); constexpr size_t hash_value(const Color& val) { - return val.color == Color::RGB ? - hash_values(val.color, val.r, val.g, val.b) + return val.isRGB() ? + hash_values(val.a, val.r, val.g, val.b) : hash_value(val.color); } diff --git a/src/commands.cc b/src/commands.cc index c77c094c..30113af2 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -2328,7 +2328,7 @@ const CommandDesc set_face_cmd = { "set-face <scope> <name> <facespec>: set face <name> to refer to <facespec> in <scope>\n" "\n" "facespec format is <fg color>[,<bg color>][+<attributes>][@<base>]\n" - "colors are either a color name, or rgb:###### values.\n" + "colors are either a color name, rgb:######, or rgba:######## values.\n" "attributes is a combination of:\n" " u: underline, i: italic, b: bold, r: reverse,\n" " B: blink, d: dim,\n" diff --git a/src/face.hh b/src/face.hh index 962b8076..442493d3 100644 --- a/src/face.hh +++ b/src/face.hh @@ -50,6 +50,14 @@ struct Face inline Face merge_faces(const Face& base, const Face& face) { + auto alpha_blend = [](Color base, Color color) { + auto blend = [&](unsigned char Color::*field) { + int blended = (base.*field * (255 - color.a) + color.*field * color.a) / 255; + return static_cast<unsigned char>(blended <= 255 ? blended : 255); + }; + return Color{blend(&Color::r), blend(&Color::g), blend(&Color::b), base.a}; + }; + auto choose = [&](Color Face::*color, Attribute final_attr) { if (face.attributes & final_attr) return face.*color; @@ -57,6 +65,8 @@ inline Face merge_faces(const Face& base, const Face& face) return base.*color; if (face.*color == Color::Default) return base.*color; + if ((base.*color).isRGB() and (face.*color).isRGB() and (face.*color).a != 255) + return alpha_blend(base.*color, face.*color); return face.*color; }; diff --git a/src/ncurses_ui.cc b/src/ncurses_ui.cc index 7644214b..4783b823 100644 --- a/src/ncurses_ui.cc +++ b/src/ncurses_ui.cc @@ -252,7 +252,7 @@ int NCursesUI::Palette::get_color(Color color) return it->value; else if (m_change_colors and can_change_color() and COLORS > 16) { - kak_assert(color.color == Color::RGB); + kak_assert(color.isRGB()); if (m_next_color > COLORS) m_next_color = 16; init_color(m_next_color, @@ -264,7 +264,7 @@ int NCursesUI::Palette::get_color(Color color) } else { - kak_assert(color.color == Color::RGB); + kak_assert(color.isRGB()); int lowestDist = INT_MAX; int closestCol = -1; for (int i = 0; i < std::min(256, COLORS); ++i) diff --git a/src/remote.cc b/src/remote.cc index 6b03edbd..8e475e72 100644 --- a/src/remote.cc +++ b/src/remote.cc @@ -127,7 +127,7 @@ private: void write_field(Color color) { write_field(color.color); - if (color.color == Color::RGB) + if (color.isRGB()) { write_field(color.r); write_field(color.g); @@ -305,7 +305,7 @@ struct MsgReader::Reader<Color> { { Color res; res.color = Reader<Color::NamedColor>::read(reader); - if (res.color == Color::RGB) + if (res.isRGB()) { res.r = Reader<unsigned char>::read(reader); res.g = Reader<unsigned char>::read(reader); |
