summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMaxime Coste <mawww@kakoune.org>2020-05-02 12:57:36 +1000
committerMaxime Coste <mawww@kakoune.org>2020-05-02 12:57:36 +1000
commitccecd5bd8e871b7aa89568609fd4ba7c50fd52ad (patch)
tree49978f00acc7893c2d746b3111fa8fe5083699d1 /src
parentc6906be475832558f057c4a22d00d56f128414bc (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.cc21
-rw-r--r--src/color.hh25
-rw-r--r--src/commands.cc2
-rw-r--r--src/face.hh10
-rw-r--r--src/ncurses_ui.cc4
-rw-r--r--src/remote.cc4
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);