From 5e98c49c4c943b162b7a86e076f1b5479e4e640c Mon Sep 17 00:00:00 2001 From: bi4k8 Date: Fri, 24 Feb 2023 13:13:19 +0000 Subject: [PATCH] use new Colour type for specials and gradients (#1418) * gradient, colours: use 24-bit colour depth internally on Wayland our Colour type assumes this anyway; lower colour depth should be handled by display backends * core: restore gradient color computation this was mistakenly disabled when moving to a unified Colour representation * core, gradient: use Colour type * tests: gradient: always test hsv/hcl colour computations no longer depend on the display backends we compile with, so these can be tested unconditionally now * gradients, specials: use Color type this makes specials now accept color names as well as hex codes, and removes all notions of color depth from gradient calculations --------- Co-authored-by: bi4k8 --- src/colours.cc | 54 --------------------------- src/colours.h | 8 +++- src/conky.cc | 19 +++++----- src/gradient.cc | 85 +++++++----------------------------------- src/gradient.h | 23 ++++-------- src/specials.cc | 62 +++++++++++++++++++----------- src/specials.h | 7 +++- tests/test-gradient.cc | 21 +---------- 8 files changed, 84 insertions(+), 195 deletions(-) diff --git a/src/colours.cc b/src/colours.cc index aa62f499..ab32a97e 100644 --- a/src/colours.cc +++ b/src/colours.cc @@ -29,62 +29,8 @@ #include "conky.h" #include "gui.h" #include "logging.h" -#ifdef BUILD_X11 -#include "x11.h" -#endif /*BUILD_X11*/ #include "x11-color.h" -/* precalculated: 31/255, and 63/255 */ -#define CONST_8_TO_5_BITS 0.12156862745098 -#define CONST_8_TO_6_BITS 0.247058823529412 - -short colour_depth = 0; -long redmask, greenmask, bluemask; - -void set_up_gradient() { -#ifdef BUILD_X11 - if (out_to_x.get(*state)) { - colour_depth = DisplayPlanes(display, screen); - } else -#endif /* BUILD_X11 */ - { - colour_depth = 16; - } - if (colour_depth != 24 && colour_depth != 16) { - NORM_ERR( - "using non-standard colour depth, gradients may look like a " - "lolly-pop"); - } - - redmask = 0; - greenmask = 0; - bluemask = 0; - for (int i = (colour_depth / 3) - 1; i >= 0; i--) { - redmask |= 1 << i; - greenmask |= 1 << i; - bluemask |= 1 << i; - } - if (colour_depth % 3 == 1) { greenmask |= 1 << (colour_depth / 3); } - redmask = redmask << (2 * colour_depth / 3 + colour_depth % 3); - greenmask = greenmask << (colour_depth / 3); -} - -/* adjust colour values depending on colour depth */ -unsigned int adjust_colours(unsigned int colour) { - double r, g, b; - - if (colour_depth == 0) { set_up_gradient(); } - if (colour_depth == 16) { - r = (colour & 0xff0000) >> 16; - g = (colour & 0xff00) >> 8; - b = colour & 0xff; - colour = static_cast(r * CONST_8_TO_5_BITS) << 11; - colour |= static_cast(g * CONST_8_TO_6_BITS) << 5; - colour |= static_cast(b * CONST_8_TO_5_BITS); - } - return colour; -} - static int hex_nibble_value(char c) { if (c >= '0' && c <= '9') { return c - '0'; diff --git a/src/colours.h b/src/colours.h index 11df01ad..335f8f9f 100644 --- a/src/colours.h +++ b/src/colours.h @@ -37,8 +37,6 @@ #include "x11.h" #endif /* BUILD_X11 */ -unsigned int adjust_colours(unsigned int); - struct Colour { uint8_t red; uint8_t green; @@ -46,6 +44,12 @@ struct Colour { uint8_t alpha; public: + // Compare two instances. + bool operator==(const Colour &c) const { + return c.red == red && c.green == green && c.blue == blue && + c.alpha == alpha; + } + // Express the color as a 32-bit ARGB integer (alpha in MSB). uint32_t to_argb32(void) { uint32_t out; diff --git a/src/conky.cc b/src/conky.cc index dfaaef39..1cd5b14b 100644 --- a/src/conky.cc +++ b/src/conky.cc @@ -407,9 +407,8 @@ int dpi_scale(int value) { } #ifdef BUILD_GUI -conky::gradient_factory *create_gradient_factory(int width, - unsigned long first_colour, - unsigned long last_colour) { +conky::gradient_factory *create_gradient_factory(int width, Colour first_colour, + Colour last_colour) { switch (graph_gradient_mode.get(*state)) { case RGB_GRADIENT: return new conky::rgb_gradient_factory(width, first_colour, last_colour); @@ -1283,7 +1282,7 @@ int draw_each_line_inner(char *s, int special_index, int last_special_applied) { case GRAPH: if (display_output() && display_output()->graphical()) { int h, by, i = 0, j = 0; - //int colour_idx = 0; + int colour_idx = 0; Colour last_colour = current_color; if (cur_x - text_start_x > mw && mw > 0) { break; } h = current->height; @@ -1310,18 +1309,18 @@ int draw_each_line_inner(char *s, int special_index, int last_special_applied) { /* in case we don't have a graph yet */ if (current->graph != nullptr) { - std::unique_ptr tmpcolour; + std::unique_ptr tmpcolour; - if (current->last_colour != 0 || current->first_colour != 0) { + if (current->colours_set) { auto factory = create_gradient_factory(w, current->last_colour, current->first_colour); tmpcolour = factory->create_gradient(); delete factory; } - //colour_idx = 0; + colour_idx = 0; for (i = w - 2; i > -1; i--) { - if (current->last_colour != 0 || current->first_colour != 0) { - /*if (current->tempgrad != 0) { + if (current->colours_set) { + if (current->tempgrad != 0) { set_foreground_color(tmpcolour[static_cast( static_cast(w - 2) - current->graph[j] * (w - 2) / @@ -1329,7 +1328,7 @@ int draw_each_line_inner(char *s, int special_index, int last_special_applied) { 1.0F))]); } else { set_foreground_color(tmpcolour[colour_idx++]); - }*/ + } } /* this is mugfugly, but it works */ if (display_output()) { diff --git a/src/gradient.cc b/src/gradient.cc index ef8611e3..2778b6ac 100644 --- a/src/gradient.cc +++ b/src/gradient.cc @@ -27,99 +27,42 @@ * */ #include "gradient.h" +#include "colours.h" #include "conky.h" #include "logging.h" -#ifdef BUILD_X11 -#include "gui.h" -#include "x11.h" -#endif /* BUILD_X11 */ - namespace conky { -bool gradient_factory::is_set = false; -short gradient_factory::colour_depth = 0; -long gradient_factory::mask[3]; -short gradient_factory::shift[3]; - -gradient_factory::gradient_factory(int width, unsigned long first_colour, - unsigned long last_colour) { +gradient_factory::gradient_factory(int width, Colour first_colour, + Colour last_colour) { // Make sure the width is always at least 2 this->width = std::max(2, width); this->first_colour = first_colour; this->last_colour = last_colour; - - if (!is_set) { - setup_colour_depth(); - setup_shifts(); - setup_masks(); - is_set = true; - } } -void gradient_factory::setup_shifts() { - shift[0] = (2 * colour_depth / 3 + colour_depth % 3); - shift[1] = (colour_depth / 3); - shift[2] = 0; -} - -void gradient_factory::setup_masks() { - mask[0] = mask[1] = mask[2] = 0; - - for (int i = (colour_depth / 3) - 1; i >= 0; i--) { - mask[0] |= 1 << i; - mask[1] |= 1 << i; - mask[2] |= 1 << i; - } - - if (colour_depth % 3 == 1) { mask[1] |= 1 << (colour_depth / 3); } - - for (int i = 0; i < 3; i++) { mask[i] = mask[i] << shift[i]; } -} - -void gradient_factory::setup_colour_depth() { -#ifdef BUILD_X11 - if (state == nullptr) { // testing purposes - colour_depth = 24; - } else if (out_to_x.get(*state)) { - colour_depth = DisplayPlanes(display, screen); - } else -#endif /* BUILD_X11 */ - { - colour_depth = 16; - } - - if (colour_depth != 24 && colour_depth != 16) { - NORM_ERR( - "using non-standard colour depth, " - "gradients may look like a lolly-pop"); - } -} - -void gradient_factory::convert_from_rgb(long original, long *array) { +void gradient_factory::convert_from_rgb(Colour original, long *array) { long scaled[3]; - for (int i = 0; i < 3; i++) { - auto value = (original & mask[i]) >> shift[i]; - scaled[i] = value * SCALE; - } + scaled[0] = original.red * SCALE; + scaled[1] = original.green * SCALE; + scaled[2] = original.blue * SCALE; convert_from_scaled_rgb(scaled, array); } -int gradient_factory::convert_to_rgb(long *const array) { +Colour gradient_factory::convert_to_rgb(long *const array) { long scaled_rgb[3]; - int rgb = 0; + Colour c; convert_to_scaled_rgb(array, scaled_rgb); - for (int i = 0; i < 3; i++) { - auto value = scaled_rgb[i] / SCALE; - rgb |= value << shift[i]; - } + c.red = scaled_rgb[0] / SCALE; + c.green = scaled_rgb[1] / SCALE; + c.blue = scaled_rgb[2] / SCALE; - return rgb; + return c; } gradient_factory::colour_array gradient_factory::create_gradient() { - colour_array colours(new unsigned long[width]); + colour_array colours(new Colour[width]); long first_converted[3]; long last_converted[3]; diff --git a/src/gradient.h b/src/gradient.h index ebec330d..8a76d55f 100644 --- a/src/gradient.h +++ b/src/gradient.h @@ -31,11 +31,12 @@ #define _GRADIENT_H #include +#include "colours.h" namespace conky { class gradient_factory { public: - typedef std::unique_ptr colour_array; + typedef std::unique_ptr colour_array; static const long SCALE = 512L; static const long SCALE2 = SCALE * 2; static const long SCALE4 = SCALE * 4; @@ -48,8 +49,7 @@ class gradient_factory { static const long SCALE360 = SCALE * 360; public: - gradient_factory(int width, unsigned long first_colour, - unsigned long last_colour); + gradient_factory(int width, Colour first_colour, Colour last_colour); virtual ~gradient_factory() {} colour_array create_gradient(); @@ -57,8 +57,8 @@ class gradient_factory { virtual void convert_from_scaled_rgb(long *const scaled, long *target) = 0; virtual void convert_to_scaled_rgb(long *const target, long *scaled) = 0; - void convert_from_rgb(long original, long *array); - int convert_to_rgb(long *const array); + void convert_from_rgb(Colour original, long *array); + Colour convert_to_rgb(long *const array); protected: virtual void fix_diff(long *) {} @@ -66,19 +66,10 @@ class gradient_factory { static long get_hue(long *const scaled, long chroma, long value); static long get_intermediate(long hue, long chroma); - static short colour_depth; - static long mask[3]; - static short shift[3]; - private: int width; - unsigned long first_colour; - unsigned long last_colour; - - static bool is_set; - static void setup_colour_depth(); - static void setup_masks(); - static void setup_shifts(); + Colour first_colour; + Colour last_colour; }; class rgb_gradient_factory : public gradient_factory { diff --git a/src/specials.cc b/src/specials.cc index d2ee561f..9e61b768 100644 --- a/src/specials.cc +++ b/src/specials.cc @@ -102,7 +102,8 @@ struct graph { int id; char flags; int width, height; - unsigned int first_colour, last_colour; + bool colours_set; + Colour first_colour, last_colour; double scale; char tempgrad; }; @@ -191,6 +192,13 @@ void scan_font(struct text_object *obj, const char *args) { } } +void apply_graph_colours(struct graph *g, const char *first_colour_name, + const char *last_colour_name) { + g->first_colour = parse_color(first_colour_name); + g->last_colour = parse_color(last_colour_name); + g->colours_set = true; +} + /** * parses for [height,width] [color1 color2] [scale] [-t] [-l] * @@ -207,6 +215,8 @@ char *scan_graph(struct text_object *obj, const char *args, double defscale) { char quoted_cmd[1024] = {'\0'}; /* double-quoted execgraph command */ char argstr[1024] = {'\0'}; /* args minus quoted_cmd */ char buf[1024] = {'\0'}; /* first unquoted string argument in argstr */ + char first_colour_name[1024] = {'\0'}; + char last_colour_name[1024] = {'\0'}; auto *g = static_cast(malloc(sizeof(struct graph))); memset(g, 0, sizeof(struct graph)); @@ -216,8 +226,9 @@ char *scan_graph(struct text_object *obj, const char *args, double defscale) { g->id = ++graph_count; g->width = default_graph_width.get(*state); g->height = default_graph_height.get(*state); - g->first_colour = 0; - g->last_colour = 0; + g->colours_set = false; + g->first_colour = Colour(); + g->last_colour = Colour(); g->scale = defscale; g->tempgrad = FALSE; if (args != nullptr) { @@ -264,59 +275,67 @@ char *scan_graph(struct text_object *obj, const char *args, double defscale) { /* interpret the beginning(!) of the argument string as: * '[height],[width] [color1] [color2] [scale]' * This means parameters like -t and -l may not be in the beginning */ - if (sscanf(argstr, "%d,%d %x %x %lf", &g->height, &g->width, - &g->first_colour, &g->last_colour, &g->scale) == 5) { + if (sscanf(argstr, "%d,%d %s %s %lf", &g->height, &g->width, + first_colour_name, last_colour_name, &g->scale) == 5) { + apply_graph_colours(g, first_colour_name, last_colour_name); return *quoted_cmd != 0 ? strndup(quoted_cmd, text_buffer_size.get(*state)) : nullptr; } /* [height],[width] [color1] [color2] */ g->scale = defscale; - if (sscanf(argstr, "%d,%d %x %x", &g->height, &g->width, &g->first_colour, - &g->last_colour) == 4) { + if (sscanf(argstr, "%d,%d %s %s", &g->height, &g->width, first_colour_name, + last_colour_name) == 4) { + apply_graph_colours(g, first_colour_name, last_colour_name); return *quoted_cmd != 0 ? strndup(quoted_cmd, text_buffer_size.get(*state)) : nullptr; } /* [command] [height],[width] [color1] [color2] [scale] */ - if (sscanf(argstr, "%1023s %d,%d %x %x %lf", buf, &g->height, &g->width, - &g->first_colour, &g->last_colour, &g->scale) == 6) { + if (sscanf(argstr, "%1023s %d,%d %s %s %lf", buf, &g->height, &g->width, + first_colour_name, last_colour_name, &g->scale) == 6) { + apply_graph_colours(g, first_colour_name, last_colour_name); return strndup(buf, text_buffer_size.get(*state)); } g->scale = defscale; - if (sscanf(argstr, "%1023s %d,%d %x %x", buf, &g->height, &g->width, - &g->first_colour, &g->last_colour) == 5) { + if (sscanf(argstr, "%1023s %d,%d %s %s", buf, &g->height, &g->width, + first_colour_name, last_colour_name) == 5) { + apply_graph_colours(g, first_colour_name, last_colour_name); return strndup(buf, text_buffer_size.get(*state)); } buf[0] = '\0'; g->height = default_graph_height.get(*state); g->width = default_graph_width.get(*state); - if (sscanf(argstr, "%x %x %lf", &g->first_colour, &g->last_colour, + if (sscanf(argstr, "%s %s %lf", first_colour_name, last_colour_name, &g->scale) == 3) { + apply_graph_colours(g, first_colour_name, last_colour_name); return *quoted_cmd != 0 ? strndup(quoted_cmd, text_buffer_size.get(*state)) : nullptr; } g->scale = defscale; - if (sscanf(argstr, "%x %x", &g->first_colour, &g->last_colour) == 2) { + if (sscanf(argstr, "%s %s", first_colour_name, last_colour_name) == 2) { + apply_graph_colours(g, first_colour_name, last_colour_name); return *quoted_cmd != 0 ? strndup(quoted_cmd, text_buffer_size.get(*state)) : nullptr; } - if (sscanf(argstr, "%1023s %x %x %lf", buf, &g->first_colour, - &g->last_colour, &g->scale) == 4) { + if (sscanf(argstr, "%1023s %s %s %lf", buf, first_colour_name, + last_colour_name, &g->scale) == 4) { + apply_graph_colours(g, first_colour_name, last_colour_name); return strndup(buf, text_buffer_size.get(*state)); } g->scale = defscale; - if (sscanf(argstr, "%1023s %x %x", buf, &g->first_colour, - &g->last_colour) == 3) { + if (sscanf(argstr, "%1023s %s %s", buf, first_colour_name, + last_colour_name) == 3) { + apply_graph_colours(g, first_colour_name, last_colour_name); return strndup(buf, text_buffer_size.get(*state)); } buf[0] = '\0'; - g->first_colour = 0; - g->last_colour = 0; + first_colour_name[0] = '\0'; + last_colour_name[0] = '\0'; if (sscanf(argstr, "%d,%d %lf", &g->height, &g->width, &g->scale) == 3) { return *quoted_cmd != 0 ? strndup(quoted_cmd, text_buffer_size.get(*state)) @@ -584,8 +603,9 @@ void new_graph(struct text_object *obj, char *buf, int buf_max_size, graphs[g->id] = graph; } s->height = dpi_scale(g->height); - s->first_colour = adjust_colours(g->first_colour); - s->last_colour = adjust_colours(g->last_colour); + s->colours_set = g->colours_set; + s->first_colour = g->first_colour; + s->last_colour = g->last_colour; if (g->scale != 0) { s->scaled = 0; s->scale = g->scale; diff --git a/src/specials.h b/src/specials.h index f0c96658..1ce9292a 100644 --- a/src/specials.h +++ b/src/specials.h @@ -29,6 +29,8 @@ #ifndef _SPECIALS_H #define _SPECIALS_H +#include "colours.h" + /* special stuff in text_buffer */ #define SPECIAL_CHAR '\x01' @@ -69,8 +71,9 @@ struct special_t { int graph_allocated; int scaled; /* auto adjust maximum */ int scale_log; - unsigned long first_colour; // for graph gradient - unsigned long last_colour; + bool colours_set; + Colour first_colour; // for graph gradient + Colour last_colour; short font_added; char tempgrad; struct special_t *next; diff --git a/tests/test-gradient.cc b/tests/test-gradient.cc index 269e0ef6..7b9950f9 100644 --- a/tests/test-gradient.cc +++ b/tests/test-gradient.cc @@ -28,12 +28,12 @@ #include "catch2/catch.hpp" +#include #include #include const int width = 4; -#ifdef BUILD_X11 // 24-bit color depth -const long colour = 0x996633; // brown +const Colour colour = Colour::from_argb32(0x996633); // brown const long expected_hue = 256; const long expected_value = 0x99; // max(0x99, 0x66, 0x33) const long expected_chroma = 0x66; // (0x99 - 0x33) @@ -42,17 +42,6 @@ const long expected_saturation = 122880L; const long expected_red = 0x99; const long expected_green = 0x66; const long expected_blue = 0x33; -#else // 16-bit color depth -const long colour = 0x99A6; // brown -const long expected_hue = 275; -const long expected_value = 0x13; // max(0x13, 0x0d, 0x06) -const long expected_chroma = 0x0d; // (0x1a - 0x06) -const long expected_luma = 2610173L; -const long expected_saturation = 126113L; -const long expected_red = 0x13; -const long expected_green = 0x0d; -const long expected_blue = 0x06; -#endif const long full_scale = conky::gradient_factory::SCALE360; @@ -114,11 +103,6 @@ TEST_CASE( delete factory; } -/* - * Due to lack of precision, the HSV and HCL functions are not reversible - * if color depth is less than 24-bit - */ -#ifdef BUILD_X11 SECTION("hsv_gradient_factory") { long tmp[3]; auto factory = new conky::hsv_gradient_factory(width, colour, colour); @@ -140,5 +124,4 @@ TEST_CASE( delete factory; } -#endif }