From 56ab33c9d6dc0255bf15ebd1fbc216766ffb247c Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Tue, 7 May 2013 18:52:23 +0200 Subject: [PATCH] support specifying colors with RGB components --- src/color.cc | 57 +++++++++++++++++++---------- src/color.hh | 23 +++++++++++- src/color_registry.cc | 28 +++++++------- src/display_buffer.hh | 2 +- src/highlighters.cc | 4 +- src/ncurses.cc | 85 ++++++++++++++++++++++++++++++++++--------- src/remote.cc | 25 +++++++++++++ 7 files changed, 168 insertions(+), 56 deletions(-) diff --git a/src/color.cc b/src/color.cc index f1b3ef8a..6067dcc5 100644 --- a/src/color.cc +++ b/src/color.cc @@ -2,37 +2,54 @@ #include "exception.hh" +#include + namespace Kakoune { Color str_to_color(const String& color) { - if (color == "default") return Color::Default; - if (color == "black") return Color::Black; - if (color == "red") return Color::Red; - if (color == "green") return Color::Green; - if (color == "yellow") return Color::Yellow; - if (color == "blue") return Color::Blue; - if (color == "magenta") return Color::Magenta; - if (color == "cyan") return Color::Cyan; - if (color == "white") return Color::White; + if (color == "default") return Colors::Default; + if (color == "black") return Colors::Black; + if (color == "red") return Colors::Red; + if (color == "green") return Colors::Green; + if (color == "yellow") return Colors::Yellow; + if (color == "blue") return Colors::Blue; + if (color == "magenta") return Colors::Magenta; + if (color == "cyan") return Colors::Cyan; + if (color == "white") return Colors::White; + + static const Regex rgb_regex{"rgb:[0-9a-fA-F]{6}"}; + if (boost::regex_match(color, rgb_regex)) + { + long int l = strtol(color.c_str() + 4, NULL, 16); + return { (unsigned char)((l >> 16) & 0xFF), + (unsigned char)((l >> 8) & 0xFF), + (unsigned char)(l & 0xFF) }; + } throw runtime_error("Unable to parse color '" + color + "'"); - return Color::Default; + return Colors::Default; } String color_to_str(const Color& color) { - switch (color) + switch (color.color) { - case Color::Default: return "default"; - case Color::Black: return "black"; - case Color::Red: return "red"; - case Color::Green: return "green"; - case Color::Yellow: return "yellow"; - case Color::Blue: return "blue"; - case Color::Magenta: return "magenta"; - case Color::Cyan: return "cyan"; - case Color::White: return "white"; + case Colors::Default: return "default"; + case Colors::Black: return "black"; + case Colors::Red: return "red"; + case Colors::Green: return "green"; + case Colors::Yellow: return "yellow"; + case Colors::Blue: return "blue"; + case Colors::Magenta: return "magenta"; + case Colors::Cyan: return "cyan"; + case Colors::White: return "white"; + case Colors::RGB: + { + char buffer[11]; + sprintf(buffer, "rgb:%02x%02x%02x", color.r, color.g, color.b); + return buffer; + } } kak_assert(false); return "default"; diff --git a/src/color.hh b/src/color.hh index 0cd7d18c..9cc5abf5 100644 --- a/src/color.hh +++ b/src/color.hh @@ -8,7 +8,7 @@ namespace Kakoune class String; -enum class Color : char +enum class Colors : char { Default, Black, @@ -18,7 +18,26 @@ enum class Color : char Blue, Magenta, Cyan, - White + White, + RGB, +}; + +struct Color +{ + Colors color; + unsigned char r; + unsigned char g; + unsigned char b; + + Color() : Color{Colors::Default} {} + Color(Colors c) : color{c}, r{0}, g{0}, b{0} {} + Color(unsigned char r, unsigned char g, unsigned char b) + : color{Colors::RGB}, r{r}, g{g}, b{b} {} + + bool operator==(const Color& c) const + { return color == c.color and r == c.r and g == c.g and b == c.b; } + bool operator!=(const Color& c) const + { return color != c.color or r != c.r or g != c.g or b != c.b; } }; using ColorPair = std::pair; diff --git a/src/color_registry.cc b/src/color_registry.cc index f35b3177..2b68ea22 100644 --- a/src/color_registry.cc +++ b/src/color_registry.cc @@ -15,7 +15,7 @@ const ColorPair& ColorRegistry::operator[](const String& colordesc) ColorPair colpair{ str_to_color(String(colordesc.begin(), it)), it != colordesc.end() ? str_to_color(String(it+1, colordesc.end())) - : Color::Default }; + : Colors::Default }; return (m_aliases[colordesc] = colpair); } @@ -32,7 +32,7 @@ void ColorRegistry::register_alias(const String& name, const String& colordesc, auto it = std::find(colordesc.begin(), colordesc.end(), ','); auto fg = str_to_color(String(colordesc.begin(), it)); - auto bg = Color::Default; + auto bg = Color{Colors::Default}; if (it != colordesc.end()) bg = str_to_color(String(it+1, colordesc.end())); @@ -41,18 +41,18 @@ void ColorRegistry::register_alias(const String& name, const String& colordesc, ColorRegistry::ColorRegistry() : m_aliases{ - { "PrimarySelection", { Color::Cyan, Color::Blue } }, - { "SecondarySelection", { Color::Black, Color::Blue } }, - { "PrimaryCursor", { Color::Black, Color::White } }, - { "SecondaryCursor", { Color::Black, Color::White } }, - { "LineNumbers", { Color::Black, Color::White } }, - { "MenuForeground", { Color::Blue, Color::Cyan } }, - { "MenuBackground", { Color::Cyan, Color::Blue } }, - { "Information", { Color::Black, Color::Yellow } }, - { "Error", { Color::Black, Color::Red } }, - { "StatusLine", { Color::Cyan, Color::Default } }, - { "StatusCursor", { Color::Black, Color::Cyan } }, - { "Prompt", { Color::Yellow, Color::Default} }, + { "PrimarySelection", { Colors::Cyan, Colors::Blue } }, + { "SecondarySelection", { Colors::Black, Colors::Blue } }, + { "PrimaryCursor", { Colors::Black, Colors::White } }, + { "SecondaryCursor", { Colors::Black, Colors::White } }, + { "LineNumbers", { Colors::Black, Colors::White } }, + { "MenuForeground", { Colors::Blue, Colors::Cyan } }, + { "MenuBackground", { Colors::Cyan, Colors::Blue } }, + { "Information", { Colors::Black, Colors::Yellow } }, + { "Error", { Colors::Black, Colors::Red } }, + { "StatusLine", { Colors::Cyan, Colors::Default } }, + { "StatusCursor", { Colors::Black, Colors::Cyan } }, + { "Prompt", { Colors::Yellow, Colors::Default} }, } {} diff --git a/src/display_buffer.hh b/src/display_buffer.hh index 82940259..f7abe4c3 100644 --- a/src/display_buffer.hh +++ b/src/display_buffer.hh @@ -115,7 +115,7 @@ struct DisplayAtom AtomContent content; DisplayAtom(AtomContent content, - ColorPair colors = {Color::Default, Color::Default}, + ColorPair colors = {Colors::Default, Colors::Default}, Attribute attribute = Normal) : content{std::move(content)}, colors{colors}, attribute{attribute} {} diff --git a/src/highlighters.cc b/src/highlighters.cc index aef857c5..be06189b 100644 --- a/src/highlighters.cc +++ b/src/highlighters.cc @@ -323,7 +323,7 @@ void expand_unprintable(DisplayBuffer& display_buffer) if ((it+1).underlying_iterator() != atom_it->content.end()) atom_it = line.split(atom_it, (it+1).underlying_iterator()); atom_it->content.replace(str); - atom_it->colors = { Color::Red, Color::Black }; + atom_it->colors = { Colors::Red, Colors::Black }; break; } } @@ -361,7 +361,7 @@ public: String content = it != lines.end() ? std::get<2>(*it) : empty; content += String(' ', width - content.char_length()); DisplayAtom atom{AtomContent(std::move(content))}; - atom.colors = { it != lines.end() ? std::get<1>(*it) : Color::Default , m_bg }; + atom.colors = { it != lines.end() ? std::get<1>(*it) : Colors::Default , m_bg }; line.insert(line.begin(), std::move(atom)); } } diff --git a/src/ncurses.cc b/src/ncurses.cc index 80d78e9a..f4e44d90 100644 --- a/src/ncurses.cc +++ b/src/ncurses.cc @@ -26,22 +26,73 @@ static void set_attribute(int attribute, bool on) attroff(attribute); } -static int nc_color(Color color) +static bool operator<(const Color& lhs, const Color& rhs) { - switch (color) - { - case Color::Black: return COLOR_BLACK; - case Color::Red: return COLOR_RED; - case Color::Green: return COLOR_GREEN; - case Color::Yellow: return COLOR_YELLOW; - case Color::Blue: return COLOR_BLUE; - case Color::Magenta: return COLOR_MAGENTA; - case Color::Cyan: return COLOR_CYAN; - case Color::White: return COLOR_WHITE; + if (lhs.color == rhs.color and lhs.color == Colors::RGB) + return lhs.r == rhs.r ? (lhs.g == rhs.g ? lhs.b < rhs.b + : lhs.g < rhs.g) + : lhs.r < rhs.r; + return lhs.color < rhs.color; +} - case Color::Default: - default: - return -1; +static int nc_color(const Color& color) +{ + static std::map colors = { + { Colors::Default, -1 }, + { Colors::Black, COLOR_BLACK }, + { Colors::Red, COLOR_RED }, + { Colors::Green, COLOR_GREEN }, + { Colors::Yellow, COLOR_YELLOW }, + { Colors::Blue, COLOR_BLUE }, + { Colors::Magenta, COLOR_MAGENTA }, + { Colors::Cyan, COLOR_CYAN }, + { Colors::White, COLOR_WHITE }, + }; + static int next_color = 8; + + auto it = colors.find(color); + if (it != colors.end()) + return it->second; + else if (can_change_color() and COLORS > 8) + { + kak_assert(color.color == Colors::RGB); + if (next_color > COLORS) + next_color = 8; + init_color(next_color, + color.r * 1000 / 255, + color.g * 1000 / 255, + color.b * 1000 / 255); + colors[color] = next_color; + return next_color++; + } + else + { + kak_assert(color.color == Colors::RGB); + // project to closest color. + struct BuiltinColor { int id; unsigned char r, g, b; }; + static constexpr BuiltinColor builtins[] = { + { COLOR_BLACK, 0, 0, 0 }, + { COLOR_RED, 255, 0, 0 }, + { COLOR_GREEN, 0, 255, 0 }, + { COLOR_YELLOW, 255, 255, 0 }, + { COLOR_BLUE, 0, 0, 255 }, + { COLOR_MAGENTA, 255, 0, 255 }, + { COLOR_CYAN, 0, 255, 255 }, + { COLOR_WHITE, 255, 255, 255 } + }; + auto sq = [](int x) { return x * x; }; + int lowestDist = INT_MAX; + int closestCol = -1; + for (auto& col : builtins) + { + int dist = sq(color.r - col.r) + sq(color.g - col.g) + sq(color.b - col.b); + if (dist < lowestDist) + { + lowestDist = dist; + closestCol = col.id; + } + } + return closestCol; } } @@ -68,7 +119,7 @@ static void set_color(WINDOW* window, const ColorPair colors) if (current_pair != -1) wattroff(window, COLOR_PAIR(current_pair)); - if (colors.first != Color::Default or colors.second != Color::Default) + if (colors.first != Colors::Default or colors.second != Colors::Default) { current_pair = get_color_pair(colors); wattron(window, COLOR_PAIR(current_pair)); @@ -202,7 +253,7 @@ void NCursesUI::draw(const DisplayBuffer& display_buffer, set_attribute(A_REVERSE, 0); set_attribute(A_BLINK, 0); set_attribute(A_BOLD, 0); - set_color(stdscr, { Color::Blue, Color::Default }); + set_color(stdscr, { Colors::Blue, Colors::Default }); for (;line_index < m_dimensions.line; ++line_index) { move((int)line_index, 0); @@ -321,7 +372,7 @@ void NCursesUI::draw_menu() auto menu_fg = get_color_pair(m_menu_fg); auto menu_bg = get_color_pair(m_menu_bg); - auto scroll_fg = get_color_pair({ Color::White, Color::White }); + auto scroll_fg = get_color_pair({ Colors::White, Colors::White }); auto scroll_bg = get_color_pair(m_menu_bg); wattron(m_menu_win, COLOR_PAIR(menu_bg)); diff --git a/src/remote.cc b/src/remote.cc index 7b68f6f8..94128871 100644 --- a/src/remote.cc +++ b/src/remote.cc @@ -75,6 +75,17 @@ public: write(memoryview(vec)); } + void write(const Color& color) + { + write(color.color); + if (color.color == Colors::RGB) + { + write(color.r); + write(color.g); + write(color.b); + } + } + void write(const ColorPair& colors) { write(colors.first); @@ -149,6 +160,20 @@ std::vector read_vector(int socket) return res; } +template<> +Color read(int socket) +{ + Color res; + res.color = read(socket); + if (res.color == Colors::RGB) + { + res.r = read(socket); + res.g = read(socket); + res.b = read(socket); + } + return res; +} + template<> ColorPair read(int socket) {